import GenAPI

public struct DiscoverFeed: Hashable {
    public enum Section: Identifiable, Hashable {
        public static func == (lhs: Section, rhs: Section) -> Bool {
            lhs.id == rhs.id
        }

        public func hash(into hasher: inout Hasher) {
            hasher.combine(id)
        }

        case playlist(PlaylistSection)
        case playlistList(PlaylistListSection)
        case style(StyleListSection)
        case userList(UserListSection)
        case promo(PromoSection)

        public var id: String {
            switch self {
            case .playlist(let value): return value.id
            case .playlistList(let value): return value.id
            case .style(let value): return value.id
            case .userList(let value): return value.id
            case .promo(let value): return value.id
            }
        }

        public var isEmpty: Bool {
            switch self {
            case .playlist(let value): return value.items.isEmpty
            case .playlistList(let value): return value.items.isEmpty
            case .style(let value): return value.items.isEmpty
            case .userList(let value): return value.items.isEmpty
            case .promo(let value): return value.items.isEmpty
            }
        }
    }

    public var sectionIndex: Int
    public var sections: [Section]
    public var pageSize: Int
    public var totalSections: Int

    public init(
        sectionIndex: Int,
        sections: [Section],
        totalSections: Int,
        pageSize: Int
    ) {
        self.sectionIndex = sectionIndex
        self.sections = sections
        self.totalSections = totalSections
        self.pageSize = pageSize
    }
}

extension DiscoverFeed {
    // swiftlint:disable:next cyclomatic_complexity
    init(_ response: DiscoverResp) {
        self.sectionIndex = response.startIndex
        self.totalSections = response.totalSections
        self.pageSize = response.pageSize
        var sections: [Section] = []
        for section in response.sections ?? [] {
            guard let object = section.object else { continue }
            switch object {
            case .playlistSectionSchema(let playlistSection):
                guard let playlistSection = try? PlaylistSection(playlistSection) else { continue }
                sections.append(.playlist(playlistSection))

            case .playlistListSectionSchema(let playlistListSection):
                guard let playlistListSection = try? PlaylistListSection(playlistListSection) else { continue }
                sections.append(.playlistList(playlistListSection))

            case .styleListSectionSchema(let styleListSection):
                guard let styleListSection = try? StyleListSection(styleListSection) else { continue }
                sections.append(.style(styleListSection))

            case .userListSectionSchema(let userListSection):
                guard let userListSection = try? UserListSection(userListSection) else { continue }
                sections.append(.userList(userListSection))

            case .communityPersonasSectionSchema,
                 .featuredFeedSectionSchema,
                 .shortcutSectionSchema,
                 .hooksSectionSchema,
                 .bannerSectionSchema,
                 .contestListSectionSchema:
                // Not currently handled in DiscoverFeed.Section
                continue

            case .promoSectionSchema(let promoSection):
                guard let promoSection = try? PromoSection(promoSection) else { continue }
                sections.append(.promo(promoSection))
            }
        }
        self.sections = sections
    }
}

public struct PlaylistSection: Hashable {
    public enum Constants {
        public static let followingFeedSectionId = "following_feed"
        public static let continueListeningSectionId = "continue_listening"
    }

    public var id: String
    public var title: String
    public var link: String?
    public var description: String?
    public var items: [Clip]
    public var previewItemsCount: Int
    public var options: [String]?
    public var selectedOption: String?
    public var secondaryOptions: [String]?
    public var secondarySelectedOption: String?

    public init(
        id: String,
        title: String,
        link: String? = nil,
        description: String? = nil,
        items: [Clip],
        previewItemsCount: Int,
        options: [String]? = nil,
        selectedOption: String? = nil,
        secondaryOptions: [String]? = nil,
        secondarySelectedOption: String? = nil
    ) {
        self.id = id
        self.title = title
        self.link = link
        self.description = description
        self.items = items
        self.previewItemsCount = previewItemsCount
        self.options = options
        self.selectedOption = selectedOption
        self.secondaryOptions = secondaryOptions
        self.secondarySelectedOption = secondarySelectedOption
    }

    init(_ playlistSection: PlaylistSectionSchema) throws {
        self.id = playlistSection.id
        self.title = playlistSection.title
        self.link = playlistSection.link
        self.description = playlistSection.description
        self.items = try playlistSection.items.map(Clip.init)
        self.previewItemsCount = playlistSection.previewItemsCount ?? 2
        self.options = playlistSection.options
        self.selectedOption = playlistSection.selectedOption
        self.secondaryOptions = playlistSection.secondaryOptions
        self.secondarySelectedOption = playlistSection.secondarySelectedOption
    }

    /// Returns the playlist ID if this section represents an actual playlist.
    /// Extracts ID from links like "/playlist/{id}" or "/playlist/liked".
    /// Returns nil for dynamic sections (no link) or genre sections ("/style/...").
    public var playlistId: String? {
        guard let link, link.hasPrefix("/playlist/") else { return nil }
        // Split path and extract only the immediate component after "playlist"
        let components = link.split(separator: "/")
        guard
            components.count >= 2,
            components[0] == "playlist"
        else {
            return nil
        }
        let id = String(components[1])
        return id.isEmpty ? nil : id
    }
}

public struct PlaylistListSection: Hashable {
    public var id: String
    public var title: String
    public var description: String?
    public var items: [Playlist]

    init(_ playlistListSection: PlaylistListSectionSchema) throws {
        self.id = playlistListSection.id
        self.title = playlistListSection.title
        self.description = playlistListSection.description
        self.items = try playlistListSection.items.map { try Playlist($0) }
    }
}

public struct StyleListSection: Hashable {
    public var id: String
    public var title: String
    public var description: String?
    public var items: [StyleItem]

    public init(id: String, title: String, description: String? = nil, items: [StyleItem]) {
        self.id = id
        self.title = title
        self.description = description
        self.items = items
    }

    init(_ styleListSection: StyleListSectionSchema) throws {
        self.id = styleListSection.id
        self.title = styleListSection.title
        self.description = styleListSection.description
        self.items = try styleListSection.items.map(StyleItem.init)
    }
}

public struct StyleItem: Hashable {
    public var id: String
    public var name: String
    public var imageUrl: String
    public var auraSettings: AuraSettings?
    public var redirectUrl: String?

    public init(id: String, name: String, imageUrl: String, auraSettings: AuraSettings? = nil, redirectUrl: String? = nil) {
        self.id = id
        self.name = name
        self.imageUrl = imageUrl
        self.auraSettings = auraSettings
        self.redirectUrl = redirectUrl
    }

    public init(_ styleItem: StyleItemSchema) throws {
        self.id = styleItem.id
        self.name = styleItem.name
        self.imageUrl = styleItem.imageUrl
        if let auraSettings = styleItem.auraSettings {
            self.auraSettings = AuraSettings(auraSettings)
        }
        self.redirectUrl = styleItem.redirectUrl
    }
}

public struct UserListSection: Hashable {
    public static func == (lhs: UserListSection, rhs: UserListSection) -> Bool {
        lhs.id == rhs.id
    }

    public func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    public var id: String
    public var title: String
    public var description: String?
    public var items: [SimpleProfileInfoSchema]

    init(_ userListSection: UserListSectionSchema) throws {
        self.id = userListSection.id
        self.title = userListSection.title
        self.description = userListSection.description
        self.items = try userListSection.items.map { try SimpleProfileInfoSchema(source: $0) }
    }
}

public struct PromoSection: Hashable {
    public var id: String
    public var title: String
    public var items: [PromoItem]

    init(_ promoSection: PromoSectionSchema) throws {
        self.id = promoSection.id
        self.title = promoSection.title
        self.items = try promoSection.items.map(PromoItem.init)
    }
}

public struct PromoItem: Hashable {
    public var id: String
    public var backgroundImageUrl: String?
    public var badge: PromoBadge?
    public var cardHeight: Int?
    public var primaryCta: PromoCta?
    public var primaryHeadline: PromoHeadline?
    public var secondaryCta: PromoCta?
    public var secondaryHeadline: PromoHeadline?
    public var type: PromoType?

    init(_ promoItem: PromoItemSchema) throws {
        self.id = promoItem.id
        self.backgroundImageUrl = promoItem.backgroundImageUrl
        self.badge = promoItem.badge.map(PromoBadge.init)
        self.cardHeight = promoItem.cardHeight
        self.primaryCta = promoItem.primaryCta.map(PromoCta.init)
        self.primaryHeadline = promoItem.primaryHeadline.map(PromoHeadline.init)
        self.secondaryCta = promoItem.secondaryCta.map(PromoCta.init)
        self.secondaryHeadline = promoItem.secondaryHeadline.map(PromoHeadline.init)
        self.type = PromoType(rawValue: "model")
    }

    public struct PromoCta: Hashable {
        public var action: String?
        public var label: String?
        public var url: String?
        public var textColor: String?
        public var backgroundColor: String?

        init(_ promoCta: PromoCtaSchema) {
            self.action = promoCta.action?.rawValue
            self.label = promoCta.label
            self.url = promoCta.url
            self.textColor = promoCta.textColor
            self.backgroundColor = promoCta.backgroundColor
        }
    }

    public struct PromoBadge: Hashable {
        public var label: String?
        public var textColor: String?
        public var backgroundColor: String?

        init(_ promoBadge: PromoBadgeSchema) {
            self.label = promoBadge.label
            self.textColor = promoBadge.textColor
            self.backgroundColor = promoBadge.backgroundColor
        }
    }

    public struct PromoHeadline: Hashable {
        public var text: String?
        public var color: String?

        init(_ promoHeadline: PromoHeadlineSchema) {
            self.text = promoHeadline.text
            self.color = promoHeadline.color
        }
    }
    
    public enum PromoType: String {
        case model = "model"
        case contest = "contest"
    }
}

public struct AuraSettings: Hashable {
    public let colorSet: AuraColorSet
    public let style: AuraStyle
    public let maskStyle: MaskStyle
    public let textPlacement: TextPlacement
    public let frameShape: FrameShape
    public let overlayColor: OverlayColor

    init(_ remote: Components.Schemas.AuraSettings) {
        self.colorSet = AuraColorSet(rawValue: remote.color_set.rawValue) ?? .redVioletBlue
        self.style = AuraStyle(rawValue: remote.style.rawValue) ?? .null
        self.maskStyle = MaskStyle(rawValue: remote.mask_style.rawValue) ?? .topLeft
        self.textPlacement = TextPlacement(rawValue: remote.text_placement.rawValue) ?? .topLeft
        self.frameShape = FrameShape(rawValue: remote.frame_shape.rawValue) ?? .square
        self.overlayColor = OverlayColor(rawValue: remote.overlay_color.rawValue) ?? .null
    }

    init(_ auraSettings: GenAPI.AuraSettings) {
        self.colorSet = AuraColorSet(rawValue: auraSettings.colorSet) ?? .redVioletBlue
        self.style = AuraStyle(rawValue: auraSettings.style) ?? .null
        self.maskStyle = MaskStyle(rawValue: auraSettings.maskStyle) ?? .topLeft
        self.textPlacement = TextPlacement(rawValue: auraSettings.textPlacement) ?? .topLeft
        self.frameShape = FrameShape(rawValue: auraSettings.frameShape) ?? .square
        self.overlayColor = OverlayColor(rawValue: auraSettings.overlayColor) ?? .null
    }
}

public enum AuraColorSet: Int {
    case redVioletBlue = 1
    case tanLaserBluePink = 2
    case cherryYellowMoss = 3
    case goldMossPink = 4
    case orangeMauveLime = 5
    case yellowOrangeBlue = 6
    case mauveVioletGreen = 7
    case fadedBlueGoldSand = 8
    case maroonVioletRed = 9
    case indigoBlueYellow = 10
    case purpleOrangePurple = 11

    public var name: String {
        switch self {
        case .redVioletBlue: return "redVioletBlue"
        case .tanLaserBluePink: return "tanLaserBluePink"
        case .cherryYellowMoss: return "cherryYellowMoss"
        case .goldMossPink: return "goldMossPink"
        case .orangeMauveLime: return "orangeMauveLime"
        case .yellowOrangeBlue: return "yellowOrangeBlue"
        case .mauveVioletGreen: return "mauveVioletGreen"
        case .fadedBlueGoldSand: return "fadedBlueGoldSand"
        case .maroonVioletRed: return "maroonVioletRed"
        case .indigoBlueYellow: return "indigoBlueYellow"
        case .purpleOrangePurple: return "purpleOrangePurple"
        }
    }
}

// AuraShape in designs
public enum AuraStyle: Int {
    case null = 1
    case cornerRipple = 2
    case rightArrow = 3
    case bottomRipple = 4
    case curveEcho = 5
    case circleTile = 6
    case diamond = 7
    case sunburst = 8
    case circleGlass = 9
    case circleFlower = 10
    case stars = 11
    case circleRipple = 12
    case curveRotation = 13
    case circle = 14
    case waves = 15
    case gradientDown = 16

    public var name: String {
        switch self {
        case .null: return "null"
        case .cornerRipple: return "cornerRipple"
        case .rightArrow: return "rightArrow"
        case .bottomRipple: return "bottomRipple"
        case .curveEcho: return "curveEcho"
        case .circleTile: return "circleTile"
        case .diamond: return "diamond"
        case .sunburst: return "sunburst"
        case .circleGlass: return "circleGlass"
        case .circleFlower: return "circleFlower"
        case .stars: return "stars"
        case .circleRipple: return "circleRipple"
        case .curveRotation: return "curveRotation"
        case .circle: return "circle"
        case .waves: return "waves"
        case .gradientDown: return "gradientDown"
        }
    }
}

public enum FrameShape: Int {
    case square = 1
    case round = 2

    public var name: String {
        switch self {
        case .square: return "square"
        case .round: return "round"
        }
    }
}

public enum MaskStyle: Int {
    case full = 1
    case topLeft = 2
    case bottomRight = 3
    case floatingImage = 4

    public var name: String {
        switch self {
        case .topLeft: return "topLeft"
        case .bottomRight: return "bottomRight"
        case .full: return "full"
        case .floatingImage: return "floatingImage"
        }
    }
}

public enum TextPlacement: Int {
    case topLeft = 1
    case middle = 2
    case bottomRight = 3

    public var name: String {
        switch self {
        case .topLeft: return "topLeft"
        case .middle: return "middle"
        case .bottomRight: return "bottomRight"
        }
    }
}

public enum OverlayColor: Int {
    case null = 0
    case yellow1 = 1
    case yellow2 = 2
    case yellow3 = 3
    case orange1 = 4
    case orange2 = 5
    case orange3 = 6
    case pink1 = 7
    case pink2 = 8
    case pink3 = 9
    case purple1 = 10
    case purple2 = 11
    case purple3 = 12
    case blue1 = 13
    case blue2 = 14
    case blue3 = 15
    case green1 = 16
    case green2 = 17
    case green3 = 18

    public var name: String {
        switch self {
        case .null: return "null"
        case .yellow1: return "yellow1"
        case .yellow2: return "yellow2"
        case .yellow3: return "yellow3"
        case .orange1: return "orange1"
        case .orange2: return "orange2"
        case .orange3: return "orange3"
        case .pink1: return "pink1"
        case .pink2: return "pink2"
        case .pink3: return "pink3"
        case .purple1: return "purple1"
        case .purple2: return "purple2"
        case .purple3: return "purple3"
        case .blue1: return "blue1"
        case .blue2: return "blue2"
        case .blue3: return "blue3"
        case .green1: return "green1"
        case .green2: return "green2"
        case .green3: return "green3"
        }
    }
}
