import APIClient
import ComponentLibrary
import ComposableArchitecture
import FeatureToasts
import Localization
import SwiftUI

struct WorkspacesView: View {
    let store: StoreOf<OrpheusWorkspacesReducer>
    let size: CGSize
    
    @Environment(\.safeAreaInsets) private var safeAreaInsets
    
    @State private var isFullScreen: Bool = false
    @State private var showMaskFade: Bool = false
    
    @FocusState private var isFocused: Bool
    
    private var width: CGFloat {
        let fsw = size.width
        return isFullScreen ? fsw : fsw * 0.8
    }
}

// MARK: - UI
extension WorkspacesView {
    var body: some View {
        VStack(spacing: 0) {
            searchBar
            scrollView
        }
        .frame(width: width)
        .background {
            Rectangle()
                .fill(.regularMaterial)
                .ignoresSafeArea()
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
        .ignoresSafeArea(.container, edges: .bottom)
        .animation(.interactiveSpring, value: isFullScreen)
        .onChange(of: isFocused, onChangeIsFocused)
        .onAppear(perform: onAppear)
        .toast(
            Binding(
                get: { store.toast },
                set: { store.send(.internal(.setToast($0))) }
            ),
            position: .bottom,
            padding: EdgeInsets(bottom: 16)
        )
    }
    
    private var searchBar: some View {
        HStack(spacing: 8) {
            Image.Icon.search
                .renderingMode(.template)
                .resizable()
                .scaledToFit()
                .frame(width: 16, height: 16)
                .foregroundColor(ChatConstants.Colors.Foreground.primary)
            
            TextField(
                L10n.FeatureCreateClip.workspaceSearchPlaceholder,
                text: Binding(
                    get: { store.searchQuery },
                    set: { store.send(.searchQueryChanged($0)) }
                )
            )
            .typographyV1(.workspaceTitle)
            .foregroundColor(ChatConstants.Colors.Foreground.primary)
            .tint(ChatConstants.Colors.Accent.brand)
            .focused($isFocused)
            .textFieldStyle(.plain)
            .frame(height: 20)
            .autocorrectionDisabled()
        }
        .padding(.horizontal, 12)
        .padding(.vertical, 10)
        .frame(height: 40)
        .background(ChatConstants.Colors.Background.Fog.thin)
        .overlay(
            Capsule()
                .stroke(ChatConstants.Colors.Background.secondary, lineWidth: 1)
        )
        .clipShape(.capsule)
        .allowsHitTesting(isFullScreen)
        .background {
            Color.clear
                .contentShape(.rect)
                .allowsHitTesting(!isFullScreen)
                .onTapGesture {
                    isFullScreen = true
                    isFocused = true
                }
        }
        .padding(.horizontal, 16)
        .padding(.top, 4)
    }
    
    private var scrollView: some View {
        ScrollView(content: scrollViewContent)
            .contentMargins(.horizontal, 16)
            .contentMargins(.top, 16)
            .contentMargins(.bottom, 16 + safeAreaInsets.bottom)
            .scrollIndicators(.hidden, axes: .vertical)
            .stableRefreshable {
                await store
                    .send(.fetchWorkspaces(shouldPaginate: false))
                    .finish()
            }
            .onScrollGeometryChange(for: CGFloat.self) { proxy in
                proxy.contentOffset.y + proxy.contentInsets.top
            } action: { _, newValue in
                let newShowMaskFade = newValue > 16
                if showMaskFade != newShowMaskFade {
                    showMaskFade = newShowMaskFade
                }
            }
            .mask {
                VStack(spacing: 0) {
                    LinearGradient(
                        colors: [.clear, .black],
                        startPoint: .top,
                        endPoint: .bottom
                    )
                    .frame(height: showMaskFade ? 32 : 16)
                    
                    Rectangle()
                        .fill(.black)
                }
                .animation(.snappy, value: showMaskFade)
            }
    }
    
    private func scrollViewContent() -> some View {
        LazyVStack(spacing: 16) {
            createButton
            
            if let error = store.fetchError {
                FailedView(
                    title: L10n.FeatureCreateClip.workspaceLoadErrorTitle,
                    message: error,
                    buttonTitle: L10n.FeatureCreateClip.retry,
                    action: {
                        store.send(.fetchWorkspaces(shouldPaginate: false))
                    }
                )
            } else {
                feed(workspaces: store.workspaces)
            }
        }
    }
    
    @ViewBuilder
    private func feed(workspaces: [Workspace]) -> some View {
        ForEach(workspaces, id: \.id) { workspace in
            WorkspaceCell(
                workspace: workspace,
                onTap: {
                    // TODO: select workspace
                },
                onTrash: {
                    store.send(.trashWorkspace(workspaceId: workspace.id))
                }
            )
        }
        
        if store.isFetchingWorkspaces {
            GradientSpinner()
                .frame(maxWidth: .infinity)
                .padding()
        }
        
        if store.canFetchMore {
            Color.clear
                .frame(height: 1)
                .onAppear {
                    store.send(.fetchWorkspaces(shouldPaginate: true))
                }
        }
    }
    
    private var createButton: some View {
        Button {
            // TODO: action
        } label: {
            HStack(spacing: 12) {
                ZStack {
                    RoundedRectangle(cornerRadius: 8)
                        .fill(Color.white.opacity(0.04))
                        .frame(width: 60, height: 50)
                    
                    Image.Icon.plus
                        .renderingMode(.template)
                        .resizable()
                        .scaledToFit()
                        .frame(width: 26, height: 26)
                        .foregroundColor(ChatConstants.Colors.Foreground.primary)
                }
                
                Text(L10n.FeatureCreateClip.newChat)
                    .typographyV1(.workspaceTitle)
                    .foregroundColor(ChatConstants.Colors.Foreground.primary)
                
                Spacer()
            }
            .contentShape(.rect)
        }
        .buttonStyle(.plain)
    }
}

// MARK: - Methods
extension WorkspacesView {
    private func onAppear() {
        store.send(.onAppear)
    }
    
    private func onChangeIsFocused(_ oldValue: Bool, _ newValue: Bool) {
        guard !newValue else { return }
        isFullScreen = false
    }
}

// MARK: - Previews
#Preview("With Workspaces") {
    GeometryReader { proxy in
        WorkspacesView(
            store: Store(initialState: OrpheusWorkspacesReducer.State()) {
                OrpheusWorkspacesReducer()
            } withDependencies: {
                $0.workspacesClient = .previewValue
            },
            size: proxy.size
        )
        .preferredColorScheme(.dark)
    }
}

#Preview("Loading") {
    GeometryReader { proxy in
        var state = OrpheusWorkspacesReducer.State()
        state.isFetchingWorkspaces = true
        
        return WorkspacesView(
            store: Store(initialState: state) {
                OrpheusWorkspacesReducer()
            } withDependencies: {
                $0.workspacesClient = .previewValue
            },
            size: proxy.size
        )
        .preferredColorScheme(.dark)
    }
}

#Preview("Error") {
    GeometryReader { proxy in
        var state = OrpheusWorkspacesReducer.State()
        state.allWorkspaces = []
        state.isFetchingWorkspaces = true // Prevent onAppear from triggering .fetchWorkspaces
        state.fetchError = "Network connection failed"
        
        return WorkspacesView(
            store: Store(initialState: state) {
                OrpheusWorkspacesReducer()
            } withDependencies: {
                $0.workspacesClient = .testValue
            },
            size: proxy.size
        )
        .preferredColorScheme(.dark)
    }
}

#Preview("Pagination State") {
    GeometryReader { proxy in
        var state = OrpheusWorkspacesReducer.State()
        state.allWorkspaces = [
            Workspace(
                id: "sample-1",
                name: "My Workspace",
                description: "",
                clipCount: 15,
                createdAt: Date(),
                lastUpdatedClip: Date(),
                ownerId: nil,
                shared: false
            )
        ]
        state.isFetchingWorkspaces = false
        state.totalExpected = 10 // More than current count, so canFetchMore = true
        state.currentPage = 1
        
        return WorkspacesView(
            store: Store(initialState: state) {
                OrpheusWorkspacesReducer()
            } withDependencies: {
                $0.workspacesClient = .testValue
            },
            size: proxy.size
        )
        .preferredColorScheme(.dark)
    }
}
