import UIKit

@MainActor
final class RepositoryDirectoryViewController: RefreshingTableViewController {
    private enum Mode: Int { case files, history }

    private let context: AppContext
    private let owner: String
    private let repository: String
    private let path: String
    private var mode = Mode.files
    private var rows: [RepositoryContentRow] = []
    private var history: CommitPage?
    private var currentPage: UInt32 = 0
    private var loadedFiles = false

    init(context: AppContext, owner: String, repository: String, path: String, name: String) {
        self.context = context
        self.owner = owner
        self.repository = repository
        self.path = path
        super.init()
        title = name
    }

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

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CommitCell.self, forCellReuseIdentifier: "commit")
        navigationItem.prompt = title
        let modeControl = UISegmentedControl(items: ["Files", "History"])
        modeControl.selectedSegmentIndex = mode.rawValue
        modeControl.addTarget(self, action: #selector(modeChanged(_:)), for: .valueChanged)
        modeControl.accessibilityLabel = "Directory view"
        navigationItem.titleView = modeControl
        loadContent(refreshing: false)
    }

    override func loadContent(refreshing: Bool) {
        switch mode {
        case .files: loadFiles(refreshing: refreshing)
        case .history: loadHistory(page: 1, refreshing: refreshing)
        }
    }

    override func loadMoreContent() {
        guard mode == .history else { return }
        loadHistory(page: currentPage + 1, refreshing: false)
    }

    private func loadFiles(refreshing: Bool) {
        resetPagination()
        beginLoading(refreshing: refreshing)
        loadingTask?.cancel()
        loadingTask = Task {
            do {
                let rows = try await context.core.repositoryContents(
                    owner: owner,
                    repository: repository,
                    path: path
                )
                guard !Task.isCancelled, mode == .files else {
                    endLoading()
                    return
                }
                self.rows = rows
                loadedFiles = true
                tableView.reloadData()
                updateEmptyView()
            } catch {
                if !Task.isCancelled { show(error: error) }
            }
            endLoading()
        }
    }

    private func loadHistory(page requestedPage: UInt32, refreshing: Bool) {
        if requestedPage == 1 {
            resetPagination()
            beginLoading(refreshing: refreshing)
        }
        loadingTask?.cancel()
        loadingTask = Task {
            do {
                let history = try await context.core.commits(
                    owner: owner,
                    repository: repository,
                    branch: nil,
                    path: path,
                    pages: requestedPage
                )
                guard !Task.isCancelled, mode == .history else {
                    if requestedPage == 1 { endLoading() }
                    return
                }
                self.history = history
                currentPage = requestedPage
                finishPagination(hasMore: history.hasMore)
                tableView.reloadData()
                updateEmptyView()
            } catch {
                if !Task.isCancelled {
                    show(error: error)
                    failPagination()
                }
            }
            if requestedPage == 1 { endLoading() }
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        mode == .files ? rows.count : history?.commits.count ?? 0
    }

    override func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath
    ) -> UITableViewCell {
        switch mode {
        case .files:
            let cell = tableView.dequeueReusableCell(withIdentifier: "repository-content")
                ?? UITableViewCell(style: .subtitle, reuseIdentifier: "repository-content")
            configureRepositoryContentCell(cell, row: rows[indexPath.row])
            return cell
        case .history:
            let cell = tableView.dequeueReusableCell(withIdentifier: "commit", for: indexPath) as! CommitCell
            if let history {
                cell.configure(history.commits[indexPath.row], laneCount: history.laneCount)
            }
            return cell
        }
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        mode == .history ? 86 : UITableView.automaticDimension
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        switch mode {
        case .files:
            showRepositoryContent(
                rows[indexPath.row],
                context: context,
                owner: owner,
                repository: repository,
                navigationController: navigationController
            )
        case .history:
            guard let commit = history?.commits[indexPath.row] else { return }
            navigationController?.pushViewController(
                FilesViewController(
                    context: context,
                    owner: owner,
                    repository: repository,
                    sha: commit.sha,
                    branch: commit.branchLabel
                ),
                animated: true
            )
        }
    }

    @objc private func modeChanged(_ sender: UISegmentedControl) {
        guard let mode = Mode(rawValue: sender.selectedSegmentIndex), mode != self.mode else { return }
        loadingTask?.cancel()
        endLoading()
        self.mode = mode
        resetPagination()
        tableView.backgroundView = nil
        tableView.reloadData()
        switch mode {
        case .files:
            if loadedFiles { updateEmptyView() } else { loadFiles(refreshing: false) }
        case .history:
            if let history {
                finishPagination(hasMore: history.hasMore)
                updateEmptyView()
            } else {
                loadHistory(page: 1, refreshing: false)
            }
        }
    }

    private func updateEmptyView() {
        switch mode {
        case .files:
            tableView.backgroundView = loadedFiles && rows.isEmpty
                ? EmptyBackgroundView(title: "Empty folder", detail: "This folder does not contain any files.")
                : nil
        case .history:
            tableView.backgroundView = history?.commits.isEmpty == true
                ? EmptyBackgroundView(title: "No commits", detail: "No commits affect this folder.")
                : nil
        }
    }
}
