//
//  ContentView.swift
//  MMEX
//
//  Created by Lisheng Guan on 2024/9/5.
//

import SwiftUI

struct ContentView: View {
    private static let overviewTab = 0
    private static let screenshotSampleDataKey = "MMEX_SCREENSHOT_SAMPLE_DATA"
    private static let screenshotScreenKey = "MMEX_SCREENSHOT_SCREEN"

    enum ScreenshotScreen: String {
        case overview
        case journal
        case insights
        case enter
        case manage
        case settings
    }

    @EnvironmentObject var pref: Preference
    @EnvironmentObject var vm: ViewModel
    @State private var isDocumentPickerPresented = false
    @State private var isNewDocumentPickerPresented = false
    @State private var isAttachDocumentPickerPresented = false
    @State private var isSampleDocument = false
    @State private var selectedTab = Preference.selectedTab
    @State private var isPresentingTransactionAddView = false
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    @Environment(\.verticalSizeClass) var verticalSizeClass

    @State private var isPasswordPromptVisible = false
    @State private var password = ""
    @State private var fileURL: URL?
    @State private var isPasswordValid = true // Flag to validate password
    @State private var iPadReturnTab = ContentView.overviewTab
    @State private var iPadDetailResetKey = 0

    private var screenshotScreen: ScreenshotScreen? {
        ProcessInfo.processInfo.environment[Self.screenshotScreenKey]
            .flatMap(ScreenshotScreen.init(rawValue:))
    }

    private var shouldAutoLoadSampleData: Bool {
        ProcessInfo.processInfo.environment[Self.screenshotSampleDataKey] == "1"
    }

    var body: some View {
        ZStack {
            if vm.isDatabaseConnected {
                if let screenshotScreen {
                    screenshotConnectedView(screen: screenshotScreen)
                } else {
                    connectedView
                }
            } else {
                disconnectedView
            }
        }
        
        .handlesExternalEvents(preferring: Set(["mmb", "emb"]), allowing: Set(["*"]))
        .onOpenURL { url in
            if url.pathExtension.lowercased() == "mmb" {
                vm.openDatabase(at: url)
            } else {
                // If the file is emb, prompt for password
                fileURL = url
                isPasswordPromptVisible = true
            }
        }

        .fileImporter(
            isPresented: $isDocumentPickerPresented,
            allowedContentTypes: [.mmb, .emb],
            allowsMultipleSelection: false
        ) {
            handleFileImport($0)
        }
        .sheet(isPresented: $isPasswordPromptVisible) {
            // Password input sheet
            VStack {
                Text("Password Required")
                    .font(.headline)
                    .padding()

                SecureField("Enter password", text: $password)
                    .padding()
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                    .padding(.bottom)

                if !isPasswordValid {
                    Text("Invalid password, please try again.")
                        .foregroundColor(.red)
                        .padding(.bottom)
                }

                HStack {
                    Button("Cancel") {
                        isPasswordPromptVisible = false
                    }
                    .padding()

                    Button("OK") {
                        validateAndOpenDatabase()
                    }
                    .padding()
                }
            }
            .padding()
            .frame(width: 300, height: 200)
        }

        .fileExporter(
            isPresented: $isNewDocumentPickerPresented,
            document: MMEXDocument(),
            contentType: .mmb,
            defaultFilename: isSampleDocument ? "Sample.mmb" : "Untitled.mmb"
        ) {
            handleFileExport($0)
        }

        .autocorrectionDisabled()
        .onAppear {
            guard shouldAutoLoadSampleData, !vm.isDatabaseConnected else { return }
            vm.createDatabase(at: nil, sampleData: true)
            guard vm.isDatabaseConnected else { return }
            selectedTab = horizontalSizeClass == .regular ? ContentView.overviewTab : Preference.selectedTab
        }
    }

    @ViewBuilder
    private var connectedView: some View {
        if horizontalSizeClass == .regular {
            NavigationSplitView {
                SidebarView(selectedTab: $selectedTab, onSelectTab: selectIpadTab) {
                    iPadReturnTab = selectedTab == 2 ? ContentView.overviewTab : selectedTab
                    isPresentingTransactionAddView = true
                }
            } detail: {
                NavigationStack {
                    TabContentView(
                        selectedTab: detailTabBinding,
                        isDocumentPickerPresented: $isDocumentPickerPresented,
                        isNewDocumentPickerPresented: $isNewDocumentPickerPresented,
                        isAttachDocumentPickerPresented: $isAttachDocumentPickerPresented,
                        isSampleDocument: $isSampleDocument
                    )
                }
                .id("\(detailTabSelection)-\(iPadDetailResetKey)")
            }
            .sheet(isPresented: $isPresentingTransactionAddView) {
                NavigationStack {
                    EnterView(
                        selectedTab: iPadEnterSelectionBinding
                    )
                }
            }
            .onAppear {
                if selectedTab == Preference.selectedTab {
                    selectedTab = ContentView.overviewTab
                    iPadReturnTab = ContentView.overviewTab
                }
            }
        } else {
            TabView(selection: $selectedTab) {
                overviewTab()
                // journalTab()
                // insightsTab()
                enterTab()
                manageTab()
                settingsTab()
            }
            .onChange(of: selectedTab) { _, tab in
                if tab == 2 { isPresentingTransactionAddView = true }
            }
        }
    }

    private var disconnectedView: some View {
        VStack(spacing: 30) {
            Image(systemName: "tray.fill")
                .resizable()
                .frame(width: 80, height: 80)
                .foregroundColor(.accentColor)
            Text("No Database Connected")
                .font(.headline)
                .padding(.bottom, 10)
            Text("Please open or create a database to get started.")
                .font(.body)
                .multilineTextAlignment(.center)
                .padding(.horizontal)

            Button(action: { isDocumentPickerPresented = true }) {
                Label("Open Database", systemImage: "folder.fill")
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)

            Button(action: { isNewDocumentPickerPresented = true; isSampleDocument = false }) {
                Label("Create New Database", systemImage: "plus.app.fill")
            }
            .buttonStyle(.bordered)
            .controlSize(.large)

            Button(action: { isNewDocumentPickerPresented = true; isSampleDocument = true }) {
                Label("Create Sample Database", systemImage: "doc.text.fill")
            }
            .buttonStyle(.bordered)
            .controlSize(.large)

            Button(action: {
                vm.createDatabase(at: nil, sampleData: true)
                guard vm.isDatabaseConnected else { return }
                log.info("Successfully created sample database in memory")
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                    selectedTab = horizontalSizeClass == .regular ? ContentView.overviewTab : Preference.selectedTab
                }
            }) {
                Label("Sample Database in Memory", systemImage: "memorychip.fill")
            }
            .buttonStyle(.bordered)
            .controlSize(.large)
        }
        .padding()
        .onAppear {
            vm.unloadAll()
        }
    }

    @ViewBuilder
    private func screenshotConnectedView(screen: ScreenshotScreen) -> some View {
        NavigationStack {
            switch screen {
            case .overview:
                OverviewView()
                    .navigationBarTitle("Overview", displayMode: .inline)
            case .journal:
                JournalView()
                    .navigationBarTitle("Journal", displayMode: .inline)
            case .insights:
                InsightsView()
                    .navigationBarTitle("Insights", displayMode: .inline)
            case .enter:
                EnterView(selectedTab: $selectedTab)
                    .navigationBarTitle("Enter", displayMode: .inline)
            case .manage:
                ManageView(
                    isDocumentPickerPresented: $isDocumentPickerPresented,
                    isNewDocumentPickerPresented: $isNewDocumentPickerPresented,
                    isAttachDocumentPickerPresented: $isAttachDocumentPickerPresented,
                    isSampleDocument: $isSampleDocument
                )
                .navigationBarTitle("Manage", displayMode: .inline)
            case .settings:
                SettingsView()
                    .navigationBarTitle("Settings", displayMode: .inline)
            }
        }
    }

    private func overviewTab() -> some View {
        NavigationStack {
            OverviewView()
                .navigationBarTitle("Overview", displayMode: .inline)
        }
        .tabItem {
            pref.theme.tab.iconText(icon: "house.fill", text: "Overview")
        }
        .tag(0)
    }

    private func journalTab() -> some View {
        NavigationView {
            if selectedTab == 0 {
                JournalView()
                    .navigationBarTitle("Journal", displayMode: .inline)
            }
        }
        .tabItem {
            pref.theme.tab.iconText(icon: "list.bullet", text: "Journal")
        }
        .tag(0)
    }

    private func insightsTab() -> some View {
        NavigationView {
            if selectedTab == 1 {
                InsightsView()
                    .navigationBarTitle("Insights", displayMode: .inline)
            }
        }
        .tabItem {
            pref.theme.tab.iconText(icon: "arrow.up.right", text: "Insights")
        }
        .tag(1)
    }

    private func enterTab() -> some View {
        NavigationView {
            EnterView(selectedTab: $selectedTab)
                .navigationBarTitle("Enter", displayMode: .inline)
        }
        .tabItem {
            pref.theme.tab.iconText(icon: "plus.circle", text: "Enter")
        }
        .tag(2)
    }

    private func manageTab() -> some View {
        // note: NavigationStack and @Environment(\.dismiss) hands the app
        NavigationView {
            ManageView(
                isDocumentPickerPresented: $isDocumentPickerPresented,
                isNewDocumentPickerPresented: $isNewDocumentPickerPresented,
                isAttachDocumentPickerPresented: $isAttachDocumentPickerPresented,
                isSampleDocument: $isSampleDocument
            )
            .navigationBarTitle("Manage", displayMode: .inline)
            //.navigationTitle("Manage")
            //.navigationBarTitleDisplayMode(.inline)
        }
        .tabItem {
            pref.theme.tab.iconText(icon: "folder", text: "Manage")
        }
        .tag(3)
    }

    // Settings tab
    private func settingsTab() -> some View {
        NavigationView {
            SettingsView()
                .navigationBarTitle("Settings", displayMode: .inline)
        }
        .tabItem {
            pref.theme.tab.iconText(icon: "gearshape", text: "Settings")
        }
        .tag(4)
    }

    // File import handling
    private func handleFileImport(_ result: Result<[URL], Error>) {
        switch result {
        case .success(let urls):
            if let url = urls.first {
                if url.pathExtension.lowercased() == "mmb" {
                    vm.openDatabase(at: url)
                    guard vm.isDatabaseConnected else { return }
                } else {
                    // Prompt for password if it's an emb file
                    fileURL = url
                    isPasswordPromptVisible = true
                }
                log.info("Successfully opened database: \(url)")
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                    selectedTab = horizontalSizeClass == .regular ? ContentView.overviewTab : Preference.selectedTab
                }
            }
        case .failure(let error):
            log.error("Failed to pick a document: \(error.localizedDescription)")
        }
    }

    // Validate and open the database using the entered password
    private func validateAndOpenDatabase() {
        guard !password.isEmpty else {
            // Invalid password, show error
            isPasswordValid = false
            return
        }
        // Open the database with the provided password
        if let url = fileURL {
            vm.openDatabase(at: url, password: password)
            guard vm.isDatabaseConnected else {
                isPasswordValid = false
                return
            }
            // Hide the password prompt
            isPasswordPromptVisible = false
        }
    }
    
    // File export handling
    private func handleFileExport(_ result: Result<URL, Error>) {
        switch result {
        case .success(let url):
            vm.createDatabase(at: url, sampleData: isSampleDocument)
            guard vm.isDatabaseConnected else { return }
            log.info("Successfully created database: \(url)")
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                selectedTab = horizontalSizeClass == .regular ? ContentView.overviewTab : Preference.selectedTab
            }
        case .failure(let error):
            log.error("Failed to pick a document: \(error.localizedDescription)")
        }
    }
}

struct SidebarView: View {
    @Binding var selectedTab: Int
    let onSelectTab: (Int) -> Void
    let onAddTransaction: () -> Void

    private let selectableTabs: [(tab: Int, title: String, systemImage: String)] = [
        (0, "Overview", "house.fill"),
        (3, "Manage", "folder"),
        (4, "Settings", "gearshape")
    ]

    var body: some View {
        VStack(spacing: 0) {
            List(selection: sidebarSelection) {
                ForEach(selectableTabs, id: \.tab) { item in
                    Label(item.title, systemImage: item.systemImage)
                        .tag(item.tab)
                }
            }
            .listStyle(SidebarListStyle())

            Divider()
                .padding(.vertical, 8)

            Button(action: onAddTransaction) {
                Label("Enter", systemImage: "plus.circle")
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(.horizontal, 20)
                    .padding(.vertical, 10)
            }
            .buttonStyle(.plain)

            Spacer(minLength: 0)
        }
    }

    private var sidebarSelection: Binding<Int?> {
        Binding(
            get: { selectedTab },
            set: { newValue in
                guard let newValue else { return }
                onSelectTab(newValue)
            }
        )
    }
}

extension ContentView {
    private func selectIpadTab(_ tab: Int) {
        selectedTab = tab
        iPadDetailResetKey += 1
    }

    private var detailTabSelection: Int {
        selectedTab == 2 ? iPadReturnTab : selectedTab
    }

    private var iPadEnterSelectionBinding: Binding<Int> {
        Binding(
            get: { selectedTab },
            set: { newValue in
                if newValue == Preference.selectedTab {
                    selectedTab = iPadReturnTab
                } else {
                    selectedTab = newValue
                }
            }
        )
    }

    private var detailTabBinding: Binding<Int> {
        Binding(
            get: { detailTabSelection },
            set: { selectedTab = $0 }
        )
    }
}

struct TabContentView: View {
    @EnvironmentObject var pref: Preference
    @EnvironmentObject var vm: ViewModel
    @Binding var selectedTab: Int
    @Binding var isDocumentPickerPresented: Bool
    @Binding var isNewDocumentPickerPresented: Bool
    @Binding var isAttachDocumentPickerPresented: Bool
    @Binding var isSampleDocument: Bool

    var body: some View {
        log.trace("TabContentView.body")
        // Use @StateObject to manage the lifecycle of TransactionViewModel
        // Here we ensure that there's no additional NavigationStack or NavigationView
        return Group {
            switch selectedTab {
            case 0:
                OverviewView()
                    .navigationBarTitle("Overview", displayMode: .inline)
            case 1:
                InsightsView()
                    .navigationBarTitle("Insights", displayMode: .inline)
            case 2:
                EnterView(selectedTab: $selectedTab)
                    .navigationBarTitle("Enter", displayMode: .inline)
            case 3:
                ManageView(
                    isDocumentPickerPresented: $isDocumentPickerPresented,
                    isNewDocumentPickerPresented: $isNewDocumentPickerPresented,
                    isAttachDocumentPickerPresented: $isAttachDocumentPickerPresented,
                    isSampleDocument: $isSampleDocument
                )
                .navigationBarTitle("Manage", displayMode: .inline)
            case 4:
                SettingsView()
                    .navigationBarTitle("Settings", displayMode: .inline)
            default:
                EmptyView()
            }
        }
        .navigationBarTitleDisplayMode(.inline) // Ensure that title is inline, preventing stacking
    }
}

#Preview("disconnected") {
    MMEXPreview.appWithoutData { pref, vm in
        ContentView(
        )
    }
}

#Preview("connected") {
    MMEXPreview.appWithSampleData { pref, vm in
        ContentView(
        )
    }
}

extension MMEXPreview {
    @ViewBuilder
    static func tab<Content: View>(
        _ title: String,
        @ViewBuilder content: @escaping (_ pref: Preference, _ vm: ViewModel) -> Content
    ) -> some View {
        MMEXPreview.appWithSampleData { pref, vm in NavigationView {
            content(pref, vm)
                .navigationBarTitle(title, displayMode: .inline)
        } }
    }
}
