import APIClient
import ComponentLibrary
import ComposableArchitecture
import FeaturePhotoPicker
import Localization
import SwiftUI
import Utilities

@Reducer
public struct ReplaceCover {
    @ObservableState
    public struct State: Equatable {
        @ObservationStateIgnored @ObservedBox var photoPickerState = PhotoPicker.State(shouldAutoDismiss: false, shouldShowDividers: true, spacing: 4.0)
        var isUpdating = false
        var clip: Clip

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

    public enum Action {
        case dismiss
        case delegate(Delegate)
        case `internal`(Internal)
        case photoPickerAction(PhotoPicker.Action)
        case didSelectImage(UIImage)

        public enum Delegate {
            case updatedClip(Clip)
        }

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

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

    public init() {}

    public var body: some ReducerOf<Self> {
        Scope(state: \.photoPickerState, action: \.photoPickerAction) {
            PhotoPicker()
        }
        Reduce { state, action in
            switch action {
            case .dismiss:
                return .run { _ in await dismiss() }

            case .photoPickerAction(.selectImage(.success(let image))):
                return .send(.didSelectImage(image))

            case .photoPickerAction:
                return .none

            case .didSelectImage(let image):
                state.isUpdating = true

                guard let base64Image = image.jpegData(compressionQuality: 0.7)?.base64EncodedString() else {
                    assertionFailure("Could not encode selected image")
                    return .none
                }

                return .run { [clip = state.clip, base64Image = base64Image] send in
                    let update = ClipMetadataSpec(imageUrl: "data:image/png;base64,\(base64Image)")
                    let result = await Result { try await api.updateClip(clip.id.remoteId, update) }
                    await send(.internal(.updateClipResponse(result)))
                }

            case .internal(.updateClipResponse(.success)):
                return .run { [clip = state.clip] send in
                    let result = await Result { try await api.getClip(clip.id.remoteId) }
                    await send(.internal(.clipResponse(result)))
                }

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

            case .internal(.clipResponse(.success(let clip))):
                state.isUpdating = false

                state.clip.imageUrl = clip.imageUrl
                state.clip.largeImageUrl = clip.largeImageUrl

                return .concatenate(
                    .send(.delegate(.updatedClip(clip))),
                    .send(.dismiss)
                )

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

            case .delegate:
                // Catch-All
                return .none

            case .internal:
                // Catch-All
                return .none
            }
        }
    }
}

public struct ReplaceCoverScreen: View {
    var store: StoreOf<ReplaceCover>

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

    public var coverSize: CGSize {
        let defaultSize = CGSize(width: 90, height: 120)
        // Compact screens use a fullscreen cover to show ReplaceCoverScreen,
        // so we use a large image instead
        return UIScreen.isCompact ? CGSize(width: defaultSize.width * 2, height: defaultSize.height * 2) : defaultSize
    }

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

                if store.isUpdating {
                    uploading
                }

                RemoteImage(url: store.clip.imageUrl, fallbackId: store.clip.id.remoteId)
                    .clipShape(.rect(cornerRadius: 8))
                    .frame(width: coverSize.width, height: coverSize.height)
                    .padding(.bottom, 8)

                Text(store.clip.title)
                    .foregroundStyle(Color.SemanticV1.textPrimary)
                    .typographyV1(.headline3)

                Spacer()

                PhotoPickerView(store: store.scope(state: \.photoPickerState, action: \.photoPickerAction))
            }
            .sheetNavigationBar(
                leading: { EmptyView() },
                center: { ToolbarTitle(L10n.FeatureClipDetail.replaceSongArt) },
                trailing: {
                    ToolbarButton(.close, background: Color.SemanticV1.backgroundQuaternary) {
                        store.send(.dismiss)
                    }
                }
            )
            .background(Color.SemanticV1.backgroundPrimary, ignoresSafeAreaEdges: .all)
            .presentationCornerRadius(40, conditional: true)
        }
    }

    private var uploading: some View {
        HStack(spacing: 8) {
            ProgressView()
                .tint(Color.SemanticV1.textInvert)
                .controlSize(.mini)
            Text(L10n.FeatureClipDetail.uploadingImage)
                .typographyV1(.body1)
                .foregroundStyle(Color.SemanticV1.textInvert)
        }
        .padding(.horizontal, 8)
        .padding(.vertical, 3)
        .background(Color.SemanticV1.iconTertiary, in: Capsule())
    }
}
