import UIKit

@MainActor
final class ServersViewController: UITableViewController {
    private let context: AppContext
    private var servers: [ServerRow] = []
    private var mutationTask: Task<Void, Never>?

    init(context: AppContext) {
        self.context = context
        super.init(style: .plain)
        title = "Servers"
        tableView.backgroundColor = .systemGroupedBackground
        tableView.separatorInset = .zero
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }

    deinit {
        mutationTask?.cancel()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        reloadServers()
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            systemItem: .add,
            primaryAction: UIAction { [weak self] _ in self?.showServerEditor() }
        )
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        servers.count
    }

    override func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath
    ) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "server")
            ?? UITableViewCell(style: .subtitle, reuseIdentifier: "server")
        let server = servers[indexPath.row]
        configureTextCell(cell, title: server.name, detail: server.url)
        cell.accessoryType = .disclosureIndicator
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        do {
            try context.selectServer(index: UInt32(indexPath.row))
        } catch {
            show(error: error)
        }
    }

    override func tableView(
        _ tableView: UITableView,
        trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath
    ) -> UISwipeActionsConfiguration? {
        let server = servers[indexPath.row]
        let deleteAction = UIContextualAction(style: .destructive, title: "Delete") {
            [weak self] _, _, completion in
            self?.confirmDelete(server, index: indexPath.row, completion: completion)
                ?? completion(false)
        }
        deleteAction.image = context.symbol("trash")

        let editAction = UIContextualAction(style: .normal, title: "Edit") {
            [weak self] _, _, completion in
            self?.showServerEditor(index: indexPath.row, completion: completion)
                ?? completion(false)
        }
        editAction.image = context.symbol("pencil")

        let configuration = UISwipeActionsConfiguration(actions: [deleteAction, editAction])
        configuration.performsFirstActionWithFullSwipe = true
        return configuration
    }

    private func reloadServers() {
        servers = context.core.servers()
        tableView.reloadData()
        tableView.backgroundView = servers.isEmpty
            ? EmptyBackgroundView(title: "No servers", detail: "Add a code hosting server to get started.")
            : nil
    }

    private func showServerEditor(
        index: Int? = nil,
        completion swipeCompletion: ((Bool) -> Void)? = nil
    ) {
        do {
            let editor = try index.map { try context.core.serverEditor(index: UInt32($0)) }
            let controller = ServerEditorViewController(
                context: context,
                index: index.map(UInt32.init),
                editor: editor
            ) { [weak self] in
                self?.reloadServers()
            }
            present(UINavigationController(rootViewController: controller), animated: true) {
                swipeCompletion?(true)
            }
        } catch {
            swipeCompletion?(false)
            show(error: error)
        }
    }

    private func confirmDelete(
        _ server: ServerRow,
        index: Int,
        completion: @escaping (Bool) -> Void
    ) {
        let alert = UIAlertController(
            title: "Delete “\(server.name)”?",
            message: "This removes the server configuration and access token from this device. It doesn’t change anything on the server.",
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completion(false) })
        alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { [weak self] _ in
            guard let self else {
                completion(false)
                return
            }
            self.mutationTask?.cancel()
            self.mutationTask = Task {
                do {
                    try await self.context.core.deleteServer(index: UInt32(index))
                    guard !Task.isCancelled else {
                        completion(false)
                        return
                    }
                    completion(true)
                    self.reloadServers()
                    self.context.reloadAfterServerChange()
                } catch {
                    completion(false)
                    if !Task.isCancelled { self.show(error: error) }
                }
            }
        })
        present(alert, animated: true)
    }
}
@MainActor
final class ServerEditorViewController: UITableViewController, UITextFieldDelegate {
    private let context: AppContext
    private let index: UInt32?
    private let editor: ServerEditor?
    private let completion: () -> Void
    private let nameField = UITextField()
    private let urlField = UITextField()
    private let tokenField = UITextField()
    private let providerButton = UIButton(type: .system)
    private var provider = ServerProvider.gitea
    private var saveButton: UIBarButtonItem!

    init(
        context: AppContext,
        index: UInt32?,
        editor: ServerEditor?,
        completion: @escaping () -> Void
    ) {
        self.context = context
        self.index = index
        self.editor = editor
        self.completion = completion
        provider = editor?.provider ?? .gitea
        super.init(style: .insetGrouped)
        title = index == nil ? "Add Server" : "Edit Server"
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) { fatalError("init(coder:) is not supported") }

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            systemItem: .cancel,
            primaryAction: UIAction { [weak self] _ in self?.dismiss(animated: true) }
        )
        saveButton = UIBarButtonItem(
            title: index == nil ? "Add" : "Save",
            style: .done,
            target: self,
            action: #selector(save)
        )
        navigationItem.rightBarButtonItem = saveButton
        configure(nameField, placeholder: "Work", contentType: .name)
        configure(urlField, placeholder: "https://gitea.example.com", contentType: .URL)
        urlField.keyboardType = .URL
        urlField.autocapitalizationType = .none
        configure(tokenField, placeholder: "Access token", contentType: nil)
        tokenField.isSecureTextEntry = true
        tokenField.autocapitalizationType = .none
        tokenField.returnKeyType = .done
        configureProviderButton()
        nameField.text = editor?.name
        urlField.text = editor?.url
        if editor != nil {
            tokenField.placeholder = "Leave unchanged"
        }
    }

    override func numberOfSections(in tableView: UITableView) -> Int { 4 }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 1 }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        ["API provider", "Name", "Server URL", "Access token"][section]
    }

    override func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath
    ) -> UITableViewCell {
        let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
        let control: UIView = indexPath.section == 0
            ? providerButton
            : [nameField, urlField, tokenField][indexPath.section - 1]
        control.translatesAutoresizingMaskIntoConstraints = false
        cell.contentView.addSubview(control)
        NSLayoutConstraint.activate([
            control.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor, constant: 16),
            control.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor, constant: -16),
            control.topAnchor.constraint(equalTo: cell.contentView.topAnchor),
            control.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor),
            cell.contentView.heightAnchor.constraint(greaterThanOrEqualToConstant: 48),
        ])
        return cell
    }

    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        if textField === nameField { urlField.becomeFirstResponder() }
        else if textField === urlField { tokenField.becomeFirstResponder() }
        else { save() }
        return true
    }

    @objc private func save() {
        view.endEditing(true)
        saveButton.isEnabled = false
        let spinner = UIActivityIndicatorView(style: .medium)
        spinner.startAnimating()
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: spinner)
        Task {
            do {
                if let index {
                    try await context.core.updateServer(
                        index: index,
                        name: nameField.text ?? "",
                        url: urlField.text ?? "",
                        token: tokenField.text ?? "",
                        provider: provider
                    )
                    context.reloadAfterServerChange()
                } else {
                    let index = try await context.core.addServer(
                        name: nameField.text ?? "",
                        url: urlField.text ?? "",
                        token: tokenField.text ?? "",
                        provider: provider
                    )
                    try context.didAddServer(index: index)
                }
                completion()
                dismiss(animated: true)
            } catch {
                navigationItem.rightBarButtonItem = saveButton
                saveButton.isEnabled = true
                show(error: error)
            }
        }
    }

    private func configure(
        _ field: UITextField,
        placeholder: String,
        contentType: UITextContentType?
    ) {
        field.placeholder = placeholder
        field.textContentType = contentType
        field.clearButtonMode = .whileEditing
        field.delegate = self
        field.returnKeyType = .next
        field.adjustsFontForContentSizeCategory = true
        field.font = .preferredFont(forTextStyle: .body)
    }

    private func configureProviderButton() {
        providerButton.contentHorizontalAlignment = .leading
        providerButton.showsMenuAsPrimaryAction = true
        providerButton.changesSelectionAsPrimaryAction = true
        providerButton.accessibilityLabel = "API provider"
        providerButton.accessibilityValue = provider == .gitea ? "Gitea" : "Forgejo"
        providerButton.menu = UIMenu(options: .singleSelection, children: [
            UIAction(title: "Gitea", state: provider == .gitea ? .on : .off) { [weak self] _ in
                self?.provider = .gitea
                self?.urlField.placeholder = "https://gitea.example.com"
                self?.providerButton.accessibilityValue = "Gitea"
            },
            UIAction(title: "Forgejo", state: provider == .forgejo ? .on : .off) { [weak self] _ in
                self?.provider = .forgejo
                self?.urlField.placeholder = "https://forgejo.example.com"
                self?.providerButton.accessibilityValue = "Forgejo"
            },
        ])
    }
}
