import SwiftUI

@MainActor
final class WorkspaceRegistry: ObservableObject {
    static let shared = WorkspaceRegistry(store: UserDefaultsWorkspaceStore())

    @Published private(set) var workspaceSummaries: [WorkspaceSummary]

    private let store: any WorkspaceStoreType
    private var controllers: [WorkspaceID: WorkspaceController] = [:]
    private let controllerFactory: @MainActor (WorkspaceSummary) -> WorkspaceController

    init(
        initialWorkspaces: [WorkspaceSummary]? = nil,
        store: (any WorkspaceStoreType)? = nil,
        controllerFactory: @escaping @MainActor (WorkspaceSummary) -> WorkspaceController = { summary in
            WorkspaceController(summary: summary)
        }
    ) {
        let resolvedStore = store ?? UserDefaultsWorkspaceStore()
        let resolvedWorkspaces = initialWorkspaces ?? resolvedStore.loadWorkspaceSummaries()

        self.store = resolvedStore
        self.controllerFactory = controllerFactory
        self.workspaceSummaries = resolvedWorkspaces

        for summary in resolvedWorkspaces {
            controllers[summary.id] = controllerFactory(summary)
        }

        _ = ensureWorkspaceExists()
    }

    var defaultWorkspaceID: WorkspaceID {
        ensureWorkspaceExists()
    }

    var defaultWorkspaceController: WorkspaceController {
        let workspaceID = ensureWorkspaceExists()
        guard let controller = controllers[workspaceID] else {
            let summary = WorkspaceSummary(id: workspaceID, name: "Main")
            let controller = controllerFactory(summary)
            controllers[workspaceID] = controller
            return controller
        }
        return controller
    }

    func allWorkspaceSummaries() -> [WorkspaceSummary] {
        workspaceSummaries
    }

    func summary(for id: WorkspaceID) -> WorkspaceSummary? {
        workspaceSummaries.first { $0.id == id }
    }

    func controller(for id: WorkspaceID) -> WorkspaceController? {
        controllers[id]
    }

    func canDeleteWorkspace(id: WorkspaceID) -> Bool {
        workspaceSummaries.count > 1 && workspaceSummaries.contains { $0.id == id }
    }

    func deletionFallbackWorkspaceID(
        forDeleting id: WorkspaceID,
        preferredFallback preferredFallbackID: WorkspaceID? = nil
    ) -> WorkspaceID? {
        let candidates = workspaceSummaries.filter { $0.id != id }

        if let preferredFallbackID,
           candidates.contains(where: { $0.id == preferredFallbackID }) {
            return preferredFallbackID
        }

        return candidates.first?.id
    }

    @discardableResult
    func ensureWorkspaceExists() -> WorkspaceID {
        if let existing = workspaceSummaries.first {
            return existing.id
        }
        return createWorkspace(named: "Main")
    }

    @discardableResult
    func createWorkspace(named name: String? = nil) -> WorkspaceID {
        let workspaceName = resolvedWorkspaceName(from: name)
        let summary = WorkspaceSummary(name: workspaceName)
        workspaceSummaries.append(summary)
        controllers[summary.id] = controllerFactory(summary)
        persistWorkspaceSummaries()
        return summary.id
    }

    func renameWorkspace(id: WorkspaceID, to name: String) {
        let trimmed = name.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return }
        guard let index = workspaceSummaries.firstIndex(where: { $0.id == id }) else { return }

        workspaceSummaries[index].name = trimmed
        controllers[id]?.rename(to: trimmed)
        persistWorkspaceSummaries()
    }

    func updateWorkspaceHotkey(id: WorkspaceID, to hotkey: HotkeyBinding?) {
        guard let index = workspaceSummaries.firstIndex(where: { $0.id == id }) else { return }
        guard workspaceSummaries[index].hotkey != hotkey else { return }

        workspaceSummaries[index].hotkey = hotkey
        controllers[id]?.updateHotkey(hotkey)
        persistWorkspaceSummaries()
    }

    func nextWorkspaceID(after id: WorkspaceID) -> WorkspaceID? {
        guard !workspaceSummaries.isEmpty else { return nil }
        guard let index = workspaceSummaries.firstIndex(where: { $0.id == id }) else {
            return workspaceSummaries.first?.id
        }

        let nextIndex = workspaceSummaries.index(after: index)
        return workspaceSummaries[nextIndex == workspaceSummaries.endIndex ? workspaceSummaries.startIndex : nextIndex].id
    }

    func previousWorkspaceID(before id: WorkspaceID) -> WorkspaceID? {
        guard !workspaceSummaries.isEmpty else { return nil }
        guard let index = workspaceSummaries.firstIndex(where: { $0.id == id }) else {
            return workspaceSummaries.last?.id
        }

        let previousIndex = index == workspaceSummaries.startIndex
            ? workspaceSummaries.index(before: workspaceSummaries.endIndex)
            : workspaceSummaries.index(before: index)
        return workspaceSummaries[previousIndex].id
    }

    @discardableResult
    func deleteWorkspace(id: WorkspaceID) -> Bool {
        guard canDeleteWorkspace(id: id) else { return false }
        workspaceSummaries.removeAll { $0.id == id }
        controllers.removeValue(forKey: id)
        _ = ensureWorkspaceExists()
        persistWorkspaceSummaries()
        return true
    }

    func updateAllWorkspacesFontSizes(_ size: CGFloat) {
        for controller in controllers.values {
            controller.updateAllFontSizes(size)
        }
    }

    func updateAllWorkspacesThemes(_ theme: TerminalTheme) {
        for controller in controllers.values {
            controller.updateAllThemes(theme)
        }
    }

    private func resolvedWorkspaceName(from proposedName: String?) -> String {
        let trimmed = proposedName?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        if !trimmed.isEmpty {
            return trimmed
        }

        let existing = Set(workspaceSummaries.map(\.name))
        if !existing.contains("Main") {
            return "Main"
        }

        var index = 2
        while existing.contains("Workspace \(index)") {
            index += 1
        }
        return "Workspace \(index)"
    }

    private func persistWorkspaceSummaries() {
        store.saveWorkspaceSummaries(workspaceSummaries)
    }
}
