import APIClient
import ComponentLibrary
import ComposableArchitecture
import Errors
import FeatureBrandedAlert
import GenAPI
import Localization
import SwiftUI
import Utilities

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

        var focusedField: Field? = .caption
        var caption: String = ""
        var isUpdating = false
        var error: AnyError?
        let clip: Clip

        public init(clip: Clip) {
            self.clip = clip
            self.caption = clip.caption ?? ""
        }
    }

    public enum Action: BindableAction {
        case binding(BindingAction<State>)
        case updateTapped
        case clearError
        case delegate(Delegate)
        case `internal`(Internal)
        case dismiss

        public enum Delegate {
            case updatedCaption(Clip)
        }

        public enum Internal {
            case updateCaptionResponse(Result<Void, Error>, Clip)
        }
    }

    @Dependency(\.apiClientV2) var api
    @Dependency(\.telemetryClient) var telemetry
    @Dependency(\.dismiss) var dismiss

    public init() {}

    public var body: some ReducerOf<Self> {
        BindingReducer()
        Reduce<State, Action> { state, action in
            switch action {
            case .binding:
                // Catch-all
                return .none

            case .clearError:
                state.error = nil
                return .none

            case .updateTapped:
                state.isUpdating = true
                return .run { [clip = state.clip, caption = state.caption] send in
                    let update = ClipMetadataSpec(caption: caption)
                    let result = await Result { try await api.updateClip(clip.id.remoteId, update) }
                    await send(.internal(.updateCaptionResponse(result, clip)))
                }

            case .internal(.updateCaptionResponse(.success, var clip)):
                clip.caption = state.caption
                return .concatenate(
                    .send(.delegate(.updatedCaption(clip))),
                    .send(.dismiss)
                )

            case .internal(.updateCaptionResponse(.failure(let error), _)):
                state.isUpdating = false
                state.error = AnyError(error)
                log.telemetry.error(error)
                return .none

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

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

public struct EditCaptionView: View {
    @Bindable var store: StoreOf<EditCaption>
    @FocusState var focusedField: EditCaption.State.Field?
    private let characterLimit = 500

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

    public var body: some View {
        VStack(spacing: 0) {
            TextField(placeholder, text: $store.caption.limit(characterLimit), axis: .vertical)
                .focused($focusedField, equals: .caption)
                .typographyV1(.headline4)
                .foregroundStyle(Color.SemanticV1.textPrimary)
                .multilineTextAlignment(.leading)
                .accentColor(.SemanticV1.textLink)

            Spacer()

            HStack {
                Spacer()
                CharCount(text: store.caption, limit: characterLimit, background: Color.SemanticV1.backgroundQuaternary)
            }
            .padding(.bottom, 20)

            PrimaryButtonV1(
                title: L10n.FeatureCaptions.save,
                isLoading: store.isUpdating,
                colorCombination: primaryButtonV1ColorCombinationDark,
                preferredSize: .medium
            ) {
                store.send(.updateTapped)
            }
            .disabled(isDisabled)
            .interactiveDismissDisabled(isDisabled)
        }
        .padding(.horizontal, 18)
        .padding(.bottom, 17)
        .sheetNavigationBar(
            leading: { EmptyView() },
            center: { ToolbarTitle(navigationTitle) },
            trailing: {
                ToolbarButton(.close, background: Color.SemanticV1.backgroundTertiary) {
                    store.send(.dismiss)
                }
                .disabled(store.isUpdating)
            }
        )
        .presentationCornerRadius(40, conditional: true)
        .background(Color.SemanticV1.backgroundPrimary)
        .bind($store.focusedField, to: $focusedField)
        .brandedAlert(error: $store.error) {
            store.send(.clearError)
        }
    }

    private var placeholder: String {
        L10n.FeatureCaptions.captionPlaceholder.appending("...")
    }

    private var navigationTitle: String {
        store.clip.caption?.ifNotEmpty == nil ? L10n.FeatureCaptions.addCaption : L10n.FeatureCaptions.editCaption
    }

    private var isDisabled: Bool {
        return store.clip.caption?.ifNotEmpty == nil && store.caption.isEmpty
    }
}
