import APIClient
import ComponentLibrary
import ComposableArchitecture
import EventBusClient
import Localization
import NavigationRouterClient
import SwiftUI
import Utilities

@Reducer
public struct RemixOf {
    @ObservableState
    public struct State: Equatable {
        let parentClip: ParentClip
        var isLoading: Bool = false
        @Shared var me: Me

        var isMyPrivateSong: Bool {
            !parentClip.isPublic && parentClip.userHandle == me.user.handle
        }

        var isOthersPrivateSong: Bool {
            !parentClip.isPublic && parentClip.userHandle != me.user.handle
        }

        public init(parentClip: ParentClip, me: Shared<Me>) {
            self.parentClip = parentClip
            self._me = me
        }
    }

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

        case dismiss
        case authorTapped(handle: String)
        case playTapped(ClipID)
        case `internal`(Internal)
    }

    public init() {}

    @Dependency(\.dismiss) var dismiss
    @Dependency(NavigationRouterClient.self) var navigationRouter
    @Dependency(\.eventBus.getOmniplayerChannel) private var getOmniplayerChannel
    @Dependency(APIClientV2.self) private var api

    public var body: some ReducerOf<Self> {
        Reduce<State, Action> { state, action in
            switch action {
            case .dismiss:
                return .run { _ in await self.dismiss() }

            case .authorTapped(handle: let handle):
                navigationRouter.send(route: .profile(handle))
                return .send(.dismiss)

            case .playTapped(let clipId):
                state.isLoading = true
                return .run { send in
                    await send(.internal(.clipResponse(Result(catching: {
                        try await api.getClip(clipId.remoteId)
                    }))))
                }

            case .internal(.clipResponse(.success(let clip))):
                state.isLoading = false
                getOmniplayerChannel()
                    .queue(
                        .playClip(clip, queue: [clip], context: SessionContext(source: .remixOfSheet(parentClipId: state.parentClip.id.remoteId)))
                    )
                return .send(.dismiss)

            case .internal(.clipResponse(.failure(let error))):
                state.isLoading = false
                // TODO: Implement some kind of error state here
                log.telemetry.error(error)
                return .none
            }
        }
    }
}

public struct RemixOfSheet: View {
    let store: StoreOf<RemixOf>

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

    public var body: some View {
        ZStack {
            // Content
            VStack(spacing: 0) {
                title

                if store.isMyPrivateSong {
                    myPrivateSongSubtitle
                        .padding(.vertical, 4)
                }

                authorButton
                    .padding(.top, 4)
                    .padding(.bottom, 12)

                if !store.isOthersPrivateSong {
                    songCard
                        .padding(.bottom, 32)

                    playButton
                        .padding(.bottom, 2)
                }
            }
            .padding(.horizontal, 20)
        }
        .sheetNavigationBar(
            leading: { EmptyView() },
            center: {
                HStack {
                    Image.Icon.remix
                        .resizable()
                        .frame(width: 20, height: 20)
                        .foregroundStyle(Color.SemanticV2.foregroundPrimary)

                    Text(L10n.FeatureClipDetail.remixOf)
                        .typographyV1(.subtitleLarge)
                        .foregroundStyle(Color.SemanticV2.foregroundPrimary)
                }
            },
            trailing: {
                ToolbarButton(
                    .close,
                    color: Color.SemanticV2.foregroundPrimary,
                    background: Color.SemanticV1.backgroundQuaternary
                ) {
                    store.send(.dismiss)
                }
            }
        )
        .presentationCornerRadius(40, conditional: true)
        .presentationBackground(Color.SemanticV1.backgroundPrimary) // Sheet background color consistency w/ the rest of Omniplayer
        .selfSizingPresentation()
    }

    private var title: some View {
        Text(store.isOthersPrivateSong ? L10n.FeatureClipDetail.privateSong : store.parentClip.title)
            .typographyV1(.displaySmall)
            .multilineTextAlignment(.center)
            .foregroundStyle(Color.SemanticV1.textPrimary)
    }

    public var myPrivateSongSubtitle: some View {
        HStack(spacing: 2) {
            Image.Icon.lockNew
                .resizable()
                .frame(width: 12, height: 12)
                .foregroundStyle(Color.SemanticV1.iconTertiary)

            Text(L10n.FeatureClipDetail.remixOfPrivateSongSubtitle)
                .typographyV1(.subtitleSmallNew)
                .multilineTextAlignment(.center)
                .foregroundStyle(Color.SemanticV1.textTertiary)
        }
    }

    @ViewBuilder
    private var authorButton: some View {
        if let displayName = store.parentClip.userDisplayName {
            Button {
                UIImpactFeedbackGenerator(style: .light).impactOccurred()
                store.send(.authorTapped(handle: store.parentClip.userHandle))
            } label: {
                HStack {
                    RemoteImage(url: store.parentClip.userAvatarImageUrl, fallbackId: store.parentClip.userDisplayName)
                        .frame(width: 20, height: 20)
                        .cornerRadius(.infinity)

                    // Need to explicitly size
                    let uiFont = TypographyV1.bodySmallNew.uiFont ?? .systemFont(ofSize: 13)
                    let uiFontWidth = displayName.boundingBox(with: uiFont).width
                    MarqueeText(
                        text: store.parentClip.userDisplayName ?? "",
                        font: TypographyV1.bodySmallNew.uiFont ?? .systemFont(ofSize: 13),
                        leftFade: 0.5,
                        rightFade: 0.5,
                        startDelay: 2,
                        alignment: .leading
                    )
                    .frame(maxWidth: uiFontWidth)
                    .foregroundStyle(Color.SemanticV1.textSecondary)
                }
            }
        }
    }

    let songCardSize = CGSize(width: 208, height: 312)
    let songCardCornerRadius: CGFloat = 24

    private var songCard: some View {
        RemoteImage(url: store.parentClip.imageUrl, fallbackId: store.parentClip.id.remoteId)
            .aspectRatio(contentMode: .fill)
            .frame(width: songCardSize.width, height: songCardSize.height)
            .clipShape(RoundedRectangle(cornerRadius: songCardCornerRadius))
            .overlay {
                RoundedRectangle(cornerRadius: songCardCornerRadius)
                    .stroke(.white, lineWidth: 1.5)
                    .mask(
                        LinearGradient(gradient: Gradient(stops: [
                            .init(color: Color.white.opacity(0.8), location: 0.0),
                            .init(color: Color.white.opacity(0.0), location: 0.5),
                        ]), startPoint: .top, endPoint: .bottom)
                    )
            }
    }

    private var playButton: some View {
        Button {
            UIImpactFeedbackGenerator(style: .light).impactOccurred()
            store.send(.playTapped(store.parentClip.id))
        } label: {
            HStack(spacing: 4) {
                Spacer()

                Text(L10n.FeatureClipDetail.play)
                    .typographyV1(.subtitleMedium)

                Image.Icon.playFilled
                    .resizable()
                    .frame(width: 16, height: 16)

                Spacer()
            }
            .foregroundStyle(Color.SemanticV1.textPrimary)
            .padding(.vertical, 12)
            .frame(maxWidth: .infinity)
            .background(Color.SemanticV1.backgroundQuaternary)
            .clipShape(.capsule)
        }
        .disabled(store.isLoading)
    }
}
