import APIClient
import CommentsClient
import ComponentLibrary
import ComposableArchitecture
import Errors
import FeatureToasts
import GenAPI
import Localization
import StatsigClient
import SwiftUI
import Utilities

public protocol SongDetailsMode {
    static var navigationTitle: String { get }
    static var submitButtonTitle: String? { get }
}

public enum EditSongDetailsMode: SongDetailsMode {
    public static var navigationTitle: String = L10n.FeatureManageClip.editSongDetailsTitle
    public static var submitButtonTitle: String?
}

public enum PublishSongMode: SongDetailsMode {
    public static var navigationTitle: String = L10n.FeatureManageClip.publishSongTitle
    public static var submitButtonTitle: String? = L10n.FeatureManageClip.publishButton
}

@ObservableState
public struct SongDetailsState<Mode: SongDetailsMode>: Equatable {
    enum Field: String, Hashable {
        case name
        case caption
        case styleSummary
    }

    @Shared(.inMemory(.commentsAccessMap)) var commentsAccessMap: ClipCommentAccessMap = .defaultValue

    @ObservableState
    public struct FieldState<T: Equatable>: Equatable {
        let initialValue: T
        let isRequired: Bool
        var currentValue: T

        public init(value: T, isRequired: Bool = false) {
            self.initialValue = value
            self.currentValue = value
            self.isRequired = isRequired
        }

        mutating func update(_ newValue: T) {
            self.currentValue = newValue
        }

        mutating func reset() {
            self.currentValue = initialValue
        }

        var hasChanges: Bool {
            return initialValue != currentValue
        }

        var isEmpty: Bool {
            if let stringValue = currentValue as? String {
                return stringValue.isEmpty
            }
            return false
        }
    }

    @ObservableState
    public enum ToggleState: Equatable {
        case idle(Bool)
        case updating(Bool)
        case error(AnyError)

        var currentValue: Bool {
            switch self {
            case .idle(let value): return value
            case .updating(let value): return value
            case .error: return false
            }
        }
    }

    var focusedField: Field?
    var nameField: FieldState<String>
    var captionField: FieldState<String>
    var styleSummaryField: FieldState<String>
    var hasPendingPinToProfileChanges = false
    var isSaving = false
    var isPublishing = false
    var error: AnyError?
    var toast: ToastType?
    var clip: Clip
    var toggleHapticSuccess: Bool = false
    var toggleHapticError: Bool = false
    var showRemixOriginTooltip: Bool = false
    var showStyleSummaryTooltip: Bool = false
    var showRemixOriginToggle: ToggleState

    var userMentionSearchSuggestions: [SimpleProfile] = []
    var userMentions: [CaptionUserMention] = []

    @Presents public var destination: SongDetailsReducer<Mode>.Destination.State?

    @Shared var me: Me

    var parentClip: ParentClip? {
        @Shared(.inMemory(.clipParentMap)) var clipParentMap: [ClipID: ParentClip] = [:]
        return clipParentMap[clip.id]
    }

    var canToggleShowRemixOrigin: Bool {
        me.user.handle == parentClip?.userHandle
    }

    public init(clip: Clip, me: Shared<Me>) {
        self.clip = clip
        self.nameField = FieldState(value: clip.title, isRequired: true)
        self.captionField = FieldState(value: clip.caption ?? "", isRequired: false)
        self.styleSummaryField = FieldState(value: clip.displayTags ?? "", isRequired: false)
        self._me = me
        self.showRemixOriginToggle = .idle(clip.showRemix)
    }

    var hasUnsavedChanges: Bool {
        nameField.hasChanges || captionField.hasChanges || styleSummaryField.hasChanges
    }

    public var coverArtRefreshId: String {
        // Force refresh when URL changes
        clip.id.remoteId + "_" + (clip.videoCoverUrl ?? clip.imageUrl)
    }

    // Only show visibility options in the Publish flow
    // or if the clip is public
    var showMoreOptions: Bool {
        if Mode.self == PublishSongMode.self {
            return true
        } else if clip.isPublic {
            return true
        } else {
            return false
        }
    }
}

// Type aliases for convenience
public typealias EditSongDetailsState = SongDetailsState<EditSongDetailsMode>
public typealias PublishSongState = SongDetailsState<PublishSongMode>
