import APIClient
import ComponentLibrary
import ComposableArchitecture
import Dependencies
import Localization
import SwiftUI
import Utilities

@Reducer
public struct CreatePlaylist {
    @ObservableState
    public struct State: Equatable {
        enum Field: String, Hashable {
            case title
        }

        var focusedField: Field? = .title
        var title = ""
        var isPublic = false
        var isSaving = false
        let playlist: Playlist?
        var shouldAddToPlaylistAfterCreating: Bool?

        var navigationTitle: String {
            playlist == nil ? L10n.FeatureManagePlaylist.createAPlaylist : L10n.FeatureManagePlaylist.renamePlaylist
        }

        var saveButtonTitle: String {
            if shouldAddToPlaylistAfterCreating == true {
                L10n.FeatureManagePlaylist.createAndAddToPlaylist
            } else {
                playlist == nil ? L10n.FeatureManagePlaylist.createPlaylist : L10n.FeatureManagePlaylist.save
            }
        }

        var showsVisibilityToggle: Bool {
            playlist == nil
        }

        public init(shouldAddToPlaylistAfterCreating: Bool?) {
            self.playlist = nil
            self.shouldAddToPlaylistAfterCreating = shouldAddToPlaylistAfterCreating
        }

        public init(playlist: Playlist) {
            self.playlist = playlist
            self.title = playlist.name
        }
    }

    public enum Action: BindableAction {
        case binding(BindingAction<State>)
        case setPlaylistName(String)
        case setPlaylistVisibility(Bool)
        case savePlaylistTapped
        case `internal`(Internal)
        case delegate(Delegate)
        case dismiss

        public enum Delegate {
            case savedPlaylist(Playlist)
        }

        public enum Internal {
            case playlistResponse(Result<Playlist, Error>)
            case updatePlaylistResponse(Result<Void, Error>, Playlist)
        }
    }

    @Dependency(APIClientV2.self) private var api
    @Dependency(APIClient.self) private var apiClient
    @Dependency(\.dismiss) private var dismiss

    public init() {}

    public var body: some ReducerOf<Self> {
        Reduce { state, action in
            switch action {
            case .savePlaylistTapped:
                state.isSaving = true
                if let playlist = state.playlist {
                    return .run { [title = state.title] send in
                        await send(.internal(.updatePlaylistResponse(Result(catching: { try await apiClient.updatePlaylist(playlist.id, title) }), playlist)))
                    }
                } else {
                    return .run { [title = state.title] send in
                        await send(.internal(.playlistResponse(Result(catching: { try await api.createPlaylist(title) }))))
                    }
                }

            case let .internal(.playlistResponse(.success(playlist))):
                return .run { [isPublic = state.isPublic] send in
                    await send(.internal(.updatePlaylistResponse(Result(catching: {
                        try await apiClient.setPlaylistVisibility(playlist.id, isPublic)
                    }), playlist)))
                }

            case var .internal(.updatePlaylistResponse(.success, playlist)):
                playlist.name = state.title
                playlist.isPublic = state.isPublic
                return .concatenate(
                    .send(.delegate(.savedPlaylist(playlist))),
                    .send(.dismiss)
                )

            case let .internal(.playlistResponse(.failure(error))):
                state.isSaving = false
                log.telemetry.error(error)
                return .none

            case let .internal(.updatePlaylistResponse(.failure(error), _)):
                state.isSaving = false
                log.telemetry.error(error)
                return .none

            case let .setPlaylistName(name):
                state.title = name
                return .none

            case let .setPlaylistVisibility(isPublic):
                state.isPublic = isPublic
                return .none

            case .dismiss:
                return .run { _ in await dismiss() }

            case .binding:
                // Catch-all
                return .none

            case .delegate:
                // handled in parent
                return .none
            }
        }
    }
}

public struct CreatePlaylistScreen: View {
    @Bindable var store: StoreOf<CreatePlaylist>
    @FocusState var focusedField: CreatePlaylist.State.Field?

    public init(store: StoreOf<CreatePlaylist>) {
        self.store = store
    }

    public var body: some View {
        NavigationStack {
            VStack {
                Spacer()

                playlistNameTextField

                Spacer()

                toolbar
            }
            .sheetNavigationBar(
                leading: { EmptyView() },
                center: { ToolbarTitle(store.state.navigationTitle) },
                trailing: {
                    ToolbarButton(.close, background: Color.SemanticV1.backgroundQuaternary) {
                        store.send(.dismiss)
                    }
                }
            )
            .background(Color.SemanticV1.backgroundPrimary, ignoresSafeAreaEdges: .all)
            .allowsHitTesting(!store.isSaving)
        }
        .presentationCornerRadius(40, conditional: true)
        .bind($store.focusedField, to: $focusedField)
    }

    private var toolbar: some View {
        VStack(spacing: 28) {
            if store.showsVisibilityToggle {
                HStack {
                    Text(L10n.FeatureManagePlaylist.shareLocationsHint)
                        .typographyV1(.caption.kerning(0.28))
                        .foregroundStyle(Color.SemanticV2.foregroundTertiary)
                    Spacer()
                    Toggle(isOn: $store.isPublic.sending(\.setPlaylistVisibility)) {}
                        .tint(Color.SemanticV2.accentPink)
                        .frame(width: 42, height: 26)
                }
                .padding(.vertical, 15)
                .padding(.leading, 15)
                .padding(.trailing, 10)
            }

            Button(action: {
                store.send(.savePlaylistTapped)
            }) {
                HStack(spacing: 4) {
                    Image.Icon.plus
                        .renderingMode(.template)
                        .foregroundStyle(Color.SemanticV2.backgroundPrimary)
                        .frame(width: 20, height: 20)

                    Text(store.state.saveButtonTitle)
                        .typographyV1(.heading4.neueMontrealMedium().kerning(0.28))
                        .foregroundStyle(Color.SemanticV2.backgroundPrimary)
                }
                .frame(maxWidth: .infinity)
                .padding(16)
                .background(
                    Color.SemanticV2.foregroundPrimary
                        .clipShape(RoundedRectangle(cornerRadius: 50))
                )
            }
            .disabled(store.title.isEmpty)
        }
        .padding(12)
        .background(Color.SemanticV1.backgroundPrimary, ignoresSafeAreaEdges: .all)
    }

    private var playlistNameTextField: some View {
        TextField(L10n.FeatureManagePlaylist.namePlaceholder, text: $store.title.sending(\.setPlaylistName))
            .textInputAutocapitalization(.words)
            .focused($focusedField, equals: .title)
            .typographyV1(.headline2)
            .foregroundStyle(Color.SemanticV1.textPrimary)
            .multilineTextAlignment(.center)
            .accentColor(.SemanticV1.textPrimary)
            .padding(.horizontal, 24)
            .submitLabel(.done)
            .minimumScaleFactor(0.8)
    }
}
