import UIKit

enum DiffSource {
    case commit(owner: String, repository: String, sha: String, path: String)
    case pull(owner: String, repository: String, number: Int64, path: String)
}

@MainActor
final class DiffViewController: UIViewController {
    private let context: AppContext
    private let source: DiffSource
    private let codeView = CodeScrollView()
    private let spinner = UIActivityIndicatorView(style: .medium)
    private var loadingTask: Task<Void, Never>?

    init(context: AppContext, source: DiffSource) {
        self.context = context
        self.source = source
        super.init(nibName: nil, bundle: nil)
    }

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

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemBackground
        codeView.refreshControl = UIRefreshControl()
        codeView.refreshControl?.addTarget(self, action: #selector(reload), for: .valueChanged)
        view.addSubview(codeView)
        NSLayoutConstraint.activate([
            codeView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            codeView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            codeView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            codeView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
        ])
        beginNavigationLoading(spinner)
        reload()
    }

    deinit { loadingTask?.cancel() }

    @objc private func reload() {
        loadingTask?.cancel()
        loadingTask = Task {
            do {
                let page: DiffPage
                switch source {
                case let .commit(owner, repository, sha, path):
                    page = try await context.core.commitDiff(
                        owner: owner,
                        repository: repository,
                        sha: sha,
                        path: path
                    )
                case let .pull(owner, repository, number, path):
                    page = try await context.core.pullDiff(
                        owner: owner,
                        repository: repository,
                        number: number,
                        path: path
                    )
                }
                title = page.title
                codeView.display(diffText(page))
            } catch {
                if !Task.isCancelled { show(error: error) }
            }
            endNavigationLoading(spinner)
            codeView.refreshControl?.endRefreshing()
        }
    }

    private func diffText(_ page: DiffPage) -> NSAttributedString {
        let output = NSMutableAttributedString()
        let font = UIFont.monospacedSystemFont(ofSize: 12, weight: .regular)
        for line in page.lines {
            let text = String(format: "%4@ %4@  %@\n", line.oldNumber, line.newNumber, line.text)
            let color: UIColor
            switch line.kind {
            case .addition: color = UIColor.systemGreen.withAlphaComponent(0.16)
            case .removal: color = UIColor.systemRed.withAlphaComponent(0.16)
            case .hunk: color = UIColor.systemBlue.withAlphaComponent(0.14)
            case .header: color = UIColor.systemGray.withAlphaComponent(0.14)
            case .context: color = .clear
            }
            output.append(NSAttributedString(string: text, attributes: [
                .font: font,
                .foregroundColor: UIColor.label,
                .backgroundColor: color,
            ]))
        }
        return output
    }
}
