import APIClient
import AVFoundation
import ComponentLibrary
import ComposableArchitecture
import Foundation
import SnippetPlayerClient
import SwiftUI
import Utilities

@Reducer
public struct FullscreenPreview {
    @ObservableState
    public struct State: Equatable {
        public var clipId: ClipID
        public var clipStartTime: Double
        public var clipEndTime: Double
        public var caption: String?
        public var clip: Clip
        public var localVideoURL: URL?
        public var isPlaying: Bool = true
        public var videoVolumeLevel: Float
        public var snippetVolumeLevel: Float
        public var user: User

        public init(
            clipId: ClipID,
            clipStartTime: Double,
            clipEndTime: Double,
            caption: String? = nil,
            clip: Clip,
            localVideoURL: URL?,
            videoVolumeLevel: Float,
            snippetVolumeLevel: Float,
            user: User
        ) {
            self.clipId = clipId
            self.clipStartTime = clipStartTime
            self.clipEndTime = clipEndTime
            self.caption = caption
            self.clip = clip
            self.localVideoURL = localVideoURL
            self.videoVolumeLevel = videoVolumeLevel
            self.snippetVolumeLevel = snippetVolumeLevel
            self.user = user
        }
    }

    public enum Action {
        case task
        case onAppear
        case onDisappear
        case closeTapped
        case togglePlayPause
        case overlayAction(HookOverlayAction)
        case snippetPlayerEvent(SnippetPlayerClientEvent)
        case videoDidRestart
    }

    public enum HookOverlayAction {
        case tap
        case doubleTap(Hook)
        case remixTapped(Hook)
        case likeTapped(Hook)
        case followTapped(Hook)
        case shareTapped(Hook)
        case commentsTapped(Hook)
        case moreTapped(Hook)
        case authorTapped(String)
        case addSongTapped(Hook)
        case changePlaylistTapped(Hook)
        case songTapped(Clip)
    }

    @Dependency(\.snippetPlayerClient) var snippetPlayerClient

    public var body: some ReducerOf<Self> {
        Reduce { state, action in
            struct SnippetPlayerEventCancellableId: Hashable {}

            switch action {
            case .task:
                snippetPlayerClient.setup(true)
                snippetPlayerClient.setVolume(state.snippetVolumeLevel)
                return .merge(
                    .run { _ in
                        await snippetPlayerClient.attachToPlayer()
                    },
                    .stream(
                        snippetPlayerClient.stream(),
                        send: Action.snippetPlayerEvent,
                        cancellableId: SnippetPlayerEventCancellableId()
                    )
                )

            case .onAppear:
                state.isPlaying = true
                let snippet = ClipSnippet(
                    clip: state.clip,
                    startTime: state.clipStartTime,
                    endTime: state.clipEndTime
                )
                snippetPlayerClient.loadAndPlaySnippet(snippet)
                return .none

            case .onDisappear:
                snippetPlayerClient.pauseCurrentClip()
                return .merge(
                    .run { _ in
                        await snippetPlayerClient.detachFromPlayer()
                    },
                    .cancel(id: SnippetPlayerEventCancellableId())
                )

            case .closeTapped:
                state.isPlaying = false
                snippetPlayerClient.pauseCurrentClip()
                return .none

            case .togglePlayPause:
                state.isPlaying.toggle()
                if state.isPlaying {
                    return .run { _ in
                        await snippetPlayerClient.attachToPlayer()
                        snippetPlayerClient.playCurrentClip()
                    }
                } else {
                    snippetPlayerClient.pauseCurrentClip()
                    return .none
                }

            case .overlayAction(.tap):
                return .send(.togglePlayPause)

            case .overlayAction:
                return .none

            case .snippetPlayerEvent(let event):
                switch event {
                case .playbackStateChanged(let status):
                    state.isPlaying = (status != .paused)
                    return .none

                case .playbackTimeUpdated, .snippetChanged:
                    return .none
                }

            case .videoDidRestart:
                if state.isPlaying {
                    let cmTime = CMTime(seconds: state.clipStartTime, preferredTimescale: 1000)
                    snippetPlayerClient.seekTo(cmTime)
                }
                return .none
            }
        }
    }
}

public struct HooksPostFullscreenPreview: View {
    @Bindable public var store: StoreOf<FullscreenPreview>
    let previewAnimationNamespace: Namespace.ID

    public init(store: StoreOf<FullscreenPreview>, previewAnimationNamespace: Namespace.ID) {
        self.store = store
        self.previewAnimationNamespace = previewAnimationNamespace
    }

    private var displayHook: Hook {
        return Hook(
            id: "preview-\(store.clipId.remoteId)",
            caption: store.caption,
            clip: store.clip,
            endClipTimestamp: store.clipEndTime,
            originalClipId: store.clipId.remoteId,
            startClipTimestamp: store.clipStartTime,
            title: "Preview",
            user: SimpleProfile(from: store.user),
            userId: Int(store.user.id) ?? 0,
            videoDuration: store.clipEndTime - store.clipStartTime
        )
    }

    public var body: some View {
        ZStack {
            Color.black
                .ignoresSafeArea()

            ZStack {
                if let localVideoURL = store.localVideoURL {
                    videoPlayer(localVideoURL)
                        .ignoresSafeArea(edges: .top)
                } else {
                    Color.black
                        .ignoresSafeArea(edges: .top)
                }

                chromeOverlay
                    .allowsHitTesting(false)

                topOverlay
            }
            .cornerRadius(30, corners: [.bottomLeft, .bottomRight])
            .padding(.bottom, 60)
            .ignoresSafeArea(edges: .top)
        }
        .environment(\.colorScheme, .dark)
        .matchedGeometryEffect(id: "videoPreview", in: previewAnimationNamespace)
        .environment(\.colorScheme, .dark)
        .task {
            store.send(.task)
        }
        .onAppear {
            store.send(.onAppear)
        }
        .onDisappear {
            store.send(.onDisappear)
        }
    }

    @ViewBuilder
    var sunoLogo: some View {
        Image.Icon.logoV1
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(height: 22.0)
            .foregroundStyle(.white)
            .shadow(color: .black.opacity(0.3), radius: 2, x: 0, y: 0)
    }

    @ViewBuilder
    var closeButton: some View {
        Button(action: {
            store.send(.closeTapped)
        }) {
            Image.Icon.close
                .foregroundStyle(.white)
                .font(.system(size: 24))
        }
        .frame(width: 40, height: 40)
        .background {
            Circle()
                .fill(.ultraThinMaterial)
                .strokeBorder(Color.SemanticV2.backgroundGlassDense, lineWidth: 0.5, antialiased: false)
        }
        .clipShape(.circle)
    }

    @ViewBuilder
    private var topOverlay: some View {
        VStack {
            HStack {
                sunoLogo
                Spacer()
                closeButton
            }
            .padding(.horizontal, 16)
            Spacer()
        }
        .padding(.top, 72)
    }

    @ViewBuilder
    private func videoPlayer(_ localVideoURL: URL) -> some View {
        LoopingVideoPlayer(
            playableUrl: localVideoURL,
            restartBeforePlaying: false,
            autoPlay: false,
            isPlaying: .constant(store.isPlaying),
            isMuted: false,
            volume: store.videoVolumeLevel,
            overrideTime: .none,
            videoGravity: .resizeAspectFill,
            fallbackView: {
                Color.black
            },
            onVideoEnd: {
                store.send(.videoDidRestart)
            }
        )
        .onTapGesture {
            store.send(.togglePlayPause)
        }
    }

    @ViewBuilder
    private var chromeOverlay: some View {
        HookVideoOverlay(
            hook: displayHook,
            index: 0,
            isVisible: true,
            playbackState: store.isPlaying ? .playing : .paused,
            showFollowButton: true,
            isFollowing: displayHook.isCreatorFollowed,
            isLiked: displayHook.isLiked,
            isClipLiked: displayHook.clip?.isLiked ?? false,
            likeCount: displayHook.likeCount,
            commentCount: displayHook.commentCount,
            overlayPlaybackViewModel: .init(),
            lyrics: .empty,
            isReported: false,
            isCreatorHidden: false,
            onRemixTapped: { hook in
                store.send(.overlayAction(.remixTapped(hook)))
            },
            onLikeTapped: { hook in
                store.send(.overlayAction(.likeTapped(hook)))
            },
            onFollowTapped: { hook in
                store.send(.overlayAction(.followTapped(hook)))
            },
            onShareTapped: { hook in
                store.send(.overlayAction(.shareTapped(hook)))
            },
            onCommentsTapped: { hook in
                store.send(.overlayAction(.commentsTapped(hook)))
            },
            onMoreTapped: { hook in
                store.send(.overlayAction(.moreTapped(hook)))
            },
            onAuthorTapped: { userId in
                store.send(.overlayAction(.authorTapped(userId)))
            },
            onAddSongTapped: { hook in
                store.send(.overlayAction(.addSongTapped(hook)))
            },
            onChangePlaylistTapped: { hook in
                store.send(.overlayAction(.changePlaylistTapped(hook)))
            },
            onSongTapped: { clip in
                store.send(.overlayAction(.songTapped(clip)))
            },
            onPlayTapped: {
                store.send(.togglePlayPause)
            },
            shouldDimButtons: true,
            shouldDisableButtons: true,
            isMuted: false,
            onTapToUnmute: {
                // Do nothing
            }
        )
    }
}
