import Highlighter
import SwiftUI
import UIKit

@MainActor
final class CommentEditorViewController: UIViewController, UITextViewDelegate {
    private let context: AppContext
    private let owner: String
    private let repository: String
    private let number: Int64
    private let comment: CommentRow?
    private let saved: () -> Void
    private let scrollView = UIScrollView()
    private let modeControl = UISegmentedControl(items: ["Write", "Preview"])
    private let bodyView = UITextView()
    private let previewView = UIView()
    private let spinner = UIActivityIndicatorView(style: .medium)
    private var task: Task<Void, Never>?
    private var previewController: UIViewController?
    private var editorFont: UIFont {
        UIFontMetrics(forTextStyle: .body).scaledFont(
            for: .monospacedSystemFont(ofSize: 15, weight: .regular)
        )
    }

    init(
        context: AppContext,
        owner: String,
        repository: String,
        number: Int64,
        comment: CommentRow? = nil,
        saved: @escaping () -> Void
    ) {
        self.context = context
        self.owner = owner
        self.repository = repository
        self.number = number
        self.comment = comment
        self.saved = saved
        super.init(nibName: nil, bundle: nil)
        title = comment == nil ? "New Comment" : "Edit Comment"
    }

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

    deinit { task?.cancel() }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemGroupedBackground
        navigationItem.leftBarButtonItem = UIBarButtonItem(
            barButtonSystemItem: .cancel,
            target: self,
            action: #selector(cancel)
        )
        restoreSaveButton()

        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.keyboardDismissMode = .interactive
        view.addSubview(scrollView)
        let stack = UIStackView(arrangedSubviews: [modeControl, bodyView, previewView])
        stack.axis = .vertical
        stack.spacing = 12
        stack.translatesAutoresizingMaskIntoConstraints = false
        scrollView.addSubview(stack)

        modeControl.selectedSegmentIndex = 0
        modeControl.accessibilityLabel = "Comment editor mode"
        modeControl.addTarget(self, action: #selector(modeChanged), for: .valueChanged)
        bodyView.delegate = self
        bodyView.text = comment?.body ?? ""
        bodyView.font = editorFont
        bodyView.adjustsFontForContentSizeCategory = true
        bodyView.backgroundColor = .secondarySystemGroupedBackground
        bodyView.layer.cornerRadius = 10
        bodyView.textContainerInset = UIEdgeInsets(top: 12, left: 8, bottom: 12, right: 8)
        bodyView.autocapitalizationType = .sentences
        bodyView.accessibilityLabel = "Comment in Markdown"
        previewView.backgroundColor = .secondarySystemGroupedBackground
        previewView.layer.cornerRadius = 10
        previewView.clipsToBounds = true
        previewView.accessibilityLabel = "Comment preview"
        previewView.isHidden = true
        highlightBody()
        updateSaveButton()

        NSLayoutConstraint.activate([
            scrollView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
            scrollView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
            scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            scrollView.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor),
            stack.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor, constant: 16),
            stack.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor, constant: -16),
            stack.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor, constant: 16),
            stack.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor, constant: -16),
            stack.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: -32),
            bodyView.heightAnchor.constraint(greaterThanOrEqualToConstant: 280),
            previewView.heightAnchor.constraint(greaterThanOrEqualToConstant: 280),
        ])
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        if comment == nil { bodyView.becomeFirstResponder() }
    }

    func textViewDidChange(_ textView: UITextView) {
        highlightBody()
        updateSaveButton()
    }

    private func highlightBody() {
        let source = bodyView.text ?? ""
        let selection = bodyView.selectedRange
        let highlighter = Highlighter()
        highlighter?.setTheme(traitCollection.userInterfaceStyle == .dark ? "atom-one-dark" : "atom-one-light")
        highlighter?.theme.setCodeFont(editorFont)
        bodyView.attributedText = highlighter?.highlight(source, as: "markdown")
            ?? NSAttributedString(string: source, attributes: [
                .font: editorFont,
                .foregroundColor: UIColor.label,
            ])
        bodyView.selectedRange = selection
        bodyView.typingAttributes = [
            .font: editorFont,
            .foregroundColor: UIColor.label,
        ]
    }

    @objc private func modeChanged() {
        let preview = modeControl.selectedSegmentIndex == 1
        if preview {
            previewController?.willMove(toParent: nil)
            previewController?.view.removeFromSuperview()
            previewController?.removeFromParent()
            let controller = UIHostingController(
                rootView: RepositoryMarkdownPreview(source: bodyView.text ?? "")
            )
            addChild(controller)
            controller.view.translatesAutoresizingMaskIntoConstraints = false
            previewView.addSubview(controller.view)
            NSLayoutConstraint.activate([
                controller.view.leadingAnchor.constraint(equalTo: previewView.leadingAnchor),
                controller.view.trailingAnchor.constraint(equalTo: previewView.trailingAnchor),
                controller.view.topAnchor.constraint(equalTo: previewView.topAnchor),
                controller.view.bottomAnchor.constraint(equalTo: previewView.bottomAnchor),
            ])
            controller.didMove(toParent: self)
            previewController = controller
        }
        bodyView.isHidden = preview
        previewView.isHidden = !preview
        view.endEditing(true)
    }

    @objc private func save() {
        view.endEditing(true)
        navigationItem.leftBarButtonItem?.isEnabled = false
        navigationItem.rightBarButtonItem = UIBarButtonItem(customView: spinner)
        spinner.startAnimating()
        task?.cancel()
        task = Task {
            do {
                try await context.core.saveIssueComment(
                    owner: owner,
                    repository: repository,
                    number: number,
                    commentId: comment?.id,
                    body: bodyView.text ?? ""
                )
                guard !Task.isCancelled else { return }
                saved()
            } catch {
                if !Task.isCancelled {
                    show(error: error)
                    restoreSaveButton()
                }
            }
        }
    }

    private func restoreSaveButton() {
        spinner.stopAnimating()
        navigationItem.leftBarButtonItem?.isEnabled = true
        navigationItem.rightBarButtonItem = UIBarButtonItem(
            title: "Save",
            style: .done,
            target: self,
            action: #selector(save)
        )
        updateSaveButton()
    }

    private func updateSaveButton() {
        navigationItem.rightBarButtonItem?.isEnabled = !(bodyView.text ?? "")
            .trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
    }

    @objc private func cancel() { dismiss(animated: true) }
}
