import UIKit

final class NotificationCell: UITableViewCell {
    private let icon = UIImageView()
    private let titleLabel = UILabel()
    private let detailLabel = UILabel()
    private let metaLabel = UILabel()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        icon.preferredSymbolConfiguration = UIImage.SymbolConfiguration(textStyle: .headline)
        icon.setContentHuggingPriority(.required, for: .horizontal)
        titleLabel.font = .preferredFont(forTextStyle: .headline)
        titleLabel.numberOfLines = 2
        detailLabel.font = .preferredFont(forTextStyle: .subheadline)
        detailLabel.textColor = .secondaryLabel
        detailLabel.numberOfLines = 2
        metaLabel.font = .preferredFont(forTextStyle: .caption1)
        metaLabel.textColor = .tertiaryLabel
        [titleLabel, detailLabel, metaLabel].forEach {
            $0.adjustsFontForContentSizeCategory = true
        }
        let labels = UIStackView(arrangedSubviews: [titleLabel, detailLabel, metaLabel])
        labels.axis = .vertical
        labels.spacing = 4
        let stack = UIStackView(arrangedSubviews: [icon, labels])
        stack.alignment = .top
        stack.spacing = 12
        stack.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(stack)
        NSLayoutConstraint.activate([
            stack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            stack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8),
            stack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
            stack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10),
            icon.widthAnchor.constraint(equalToConstant: 24),
        ])
    }

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

    func configure(_ row: NotificationRow) {
        icon.image = UIImage(systemName: symbolName(for: row.target))
        icon.tintColor = row.unread ? .tintColor : .secondaryLabel
        titleLabel.text = row.title
        detailLabel.text = row.detail
        metaLabel.text = row.meta
        accessoryType = row.target == .none ? .none : .disclosureIndicator
        selectionStyle = row.target == .none ? .none : .default
        accessibilityValue = row.unread ? "Open" : "Closed"
    }

    private func symbolName(for target: ActivityTargetKind) -> String {
        switch target {
        case .repository: return "books.vertical"
        case .issue: return "exclamationmark.circle"
        case .pullRequest: return "arrow.triangle.pull"
        case .commit: return "point.topleft.down.to.point.bottomright.curvepath"
        case .none: return "bell"
        }
    }
}

@MainActor
final class NotificationsViewController: RefreshingTableViewController {
    private let context: AppContext
    private let statusControl = UISegmentedControl(items: ["Open", "Closed"])
    private var rows: [NotificationRow] = []
    private var currentPage: UInt32 = 0

    init(context: AppContext) {
        self.context = context
        super.init()
        title = "Notifications"
    }

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

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(NotificationCell.self, forCellReuseIdentifier: "notification")
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 92
        statusControl.selectedSegmentIndex = 0
        statusControl.addTarget(self, action: #selector(statusChanged), for: .valueChanged)
        statusControl.accessibilityLabel = "Notification status"
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: statusControl)
        loadContent(refreshing: false)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        guard currentPage > 0 else { return }
        loadNotifications(page: 1, refreshing: false)
    }

    override func loadContent(refreshing: Bool) {
        loadNotifications(page: 1, refreshing: refreshing)
    }

    override func loadMoreContent() {
        loadNotifications(page: currentPage + 1, refreshing: false)
    }

    private func loadNotifications(page: UInt32, refreshing: Bool) {
        if page == 1 {
            resetPagination()
            beginLoading(refreshing: refreshing)
        }
        loadingTask?.cancel()
        loadingTask = Task {
            do {
                let result = try await context.core.notifications(
                    status: statusControl.selectedSegmentIndex == 0 ? .open : .closed,
                    page: page
                )
                if page == 1 { rows = result.rows } else { rows.append(contentsOf: result.rows) }
                currentPage = page
                finishPagination(hasMore: result.hasMore)
                tableView.reloadData()
                let status = statusControl.selectedSegmentIndex == 0 ? "open" : "closed"
                tableView.backgroundView = rows.isEmpty
                    ? EmptyBackgroundView(
                        title: "No \(status) notifications",
                        detail: "This server has no \(status) notifications."
                    )
                    : nil
            } catch {
                if !Task.isCancelled {
                    show(error: error)
                    failPagination()
                }
            }
            if page == 1 { endLoading() }
        }
    }

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

    override func tableView(
        _ tableView: UITableView,
        cellForRowAt indexPath: IndexPath
    ) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(
            withIdentifier: "notification",
            for: indexPath
        ) as! NotificationCell
        cell.configure(rows[indexPath.row])
        return cell
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        let row = rows[indexPath.row]
        guard row.target != .none else { return }
        guard row.unread else {
            context.route(row)
            return
        }
        loadingTask?.cancel()
        loadingTask = Task {
            do {
                try await context.core.markNotificationRead(serverId: row.serverId, id: row.id)
                guard !Task.isCancelled else { return }
                context.route(row)
            } catch {
                if !Task.isCancelled { show(error: error) }
            }
        }
    }

    @objc private func statusChanged() {
        loadNotifications(page: 1, refreshing: false)
    }
}
