//
//  ShareViewController.swift
//  FileProviderUIExt
//
//  Created by Claudio Cambra on 21/2/24.
//

import AppKit
import FileProvider
import NextcloudKit
import OSLog
import QuickLookThumbnailing

class ShareViewController: NSViewController, ShareViewDataSourceUIDelegate {
    let shareDataSource = ShareTableViewDataSource()
    let itemIdentifiers: [NSFileProviderItemIdentifier]

    @IBOutlet weak var fileNameIcon: NSImageView!
    @IBOutlet weak var fileNameLabel: NSTextField!
    @IBOutlet weak var descriptionLabel: NSTextField!
    @IBOutlet weak var createButton: NSButton!
    @IBOutlet weak var closeButton: NSButton!
    @IBOutlet weak var tableView: NSTableView!
    @IBOutlet weak var optionsView: ShareOptionsView!
    @IBOutlet weak var splitView: NSSplitView!
    @IBOutlet weak var loadingEffectView: NSVisualEffectView!
    @IBOutlet weak var loadingIndicator: NSProgressIndicator!
    @IBOutlet weak var errorMessageStackView: NSStackView!
    @IBOutlet weak var errorTextLabel: NSTextField!
    @IBOutlet weak var noSharesLabel: NSTextField!

    public override var nibName: NSNib.Name? {
        return NSNib.Name(self.className)
    }

    var actionViewController: DocumentActionViewController! {
        return parent as? DocumentActionViewController
    }

    init(_ itemIdentifiers: [NSFileProviderItemIdentifier]) {
        self.itemIdentifiers = itemIdentifiers
        super.init(nibName: nil, bundle: nil)

        guard let firstItem = itemIdentifiers.first else {
            Logger.shareViewController.error("called without items")
            closeAction(self)
            return
        }

        Logger.shareViewController.info(
            """
            Instantiated with itemIdentifiers: 
            \(itemIdentifiers.map { $0.rawValue }, privacy: .public)
            """
        )

        Task {
            await processItemIdentifier(firstItem)
        }
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        dismissError(self)
        hideOptions(self)
    }

    @IBAction func closeAction(_ sender: Any) {
        actionViewController.extensionContext.completeRequest()
    }

    private func processItemIdentifier(_ itemIdentifier: NSFileProviderItemIdentifier) async {
        guard let manager = NSFileProviderManager(for: actionViewController.domain) else {
            fatalError("NSFileProviderManager isn't expected to fail")
        }

        do {
            let itemUrl = try await manager.getUserVisibleURL(for: itemIdentifier)
            guard itemUrl.startAccessingSecurityScopedResource() else {
                Logger.shareViewController.error("Could not access scoped resource for item url!")
                return
            }
            await updateDisplay(itemUrl: itemUrl)
            shareDataSource.uiDelegate = self
            shareDataSource.sharesTableView = tableView
            shareDataSource.loadItem(url: itemUrl)
            optionsView.dataSource = shareDataSource
            itemUrl.stopAccessingSecurityScopedResource()
        } catch let error {
            let errorString = "Error processing item: \(error)"
            Logger.shareViewController.error("\(errorString, privacy: .public)")
            fileNameLabel.stringValue = "Unknown item"
            descriptionLabel.stringValue = errorString
        }
    }

    private func updateDisplay(itemUrl: URL) async {
        fileNameLabel.stringValue = itemUrl.lastPathComponent

        let request = QLThumbnailGenerator.Request(
            fileAt: itemUrl,
            size: CGSize(width: 128, height: 128),
            scale: 1.0,
            representationTypes: .icon
        )

        let generator = QLThumbnailGenerator.shared
        let fileThumbnail = await withCheckedContinuation { continuation in
            generator.generateRepresentations(for: request) { thumbnail, type, error in
                if thumbnail == nil || error != nil {
                    Logger.shareViewController.error(
                        """
                        Could not get thumbnail: \(error, privacy: .public)
                        """
                    )
                }
                continuation.resume(returning: thumbnail)
            }
        }
        fileNameIcon.image = fileThumbnail?.nsImage

        let resourceValues = try? itemUrl.resourceValues(
            forKeys: [.fileSizeKey, .contentModificationDateKey]
        )
        var sizeDesc = "Unknown size"
        var modDesc = "Unknown modification date"
        if let fileSize = resourceValues?.fileSize {
            sizeDesc = ByteCountFormatter().string(fromByteCount: Int64(fileSize))
        }
        if let modificationDate = resourceValues?.contentModificationDate {
            let modDateString = DateFormatter.localizedString(
                from: modificationDate, dateStyle: .short, timeStyle: .short
            )
            modDesc = "Last modified: \(modDateString)"
        }
        descriptionLabel.stringValue = "\(sizeDesc) · \(modDesc)"
    }

    @IBAction func dismissError(_ sender: Any) {
        errorMessageStackView.isHidden = true
    }

    @IBAction func createShare(_ sender: Any) {
        guard let kit = shareDataSource.kit else { return }
        optionsView.kit = kit
        optionsView.createMode = true
        tableView.deselectAll(self)
        if !splitView.arrangedSubviews.contains(optionsView) {
            splitView.addArrangedSubview(optionsView)
            optionsView.isHidden = false
        }
    }

    func fetchStarted() {
        loadingEffectView.isHidden = false
        loadingIndicator.startAnimation(self)
    }

    func fetchFinished() {
        noSharesLabel.isHidden = !shareDataSource.shares.isEmpty
        loadingEffectView.isHidden = true
        loadingIndicator.stopAnimation(self)
    }

    func hideOptions(_ sender: Any) {
        if sender as? ShareTableViewDataSource == shareDataSource, optionsView.createMode {
            // Do not hide options if the table view has had everything deselected when we set the
            // options view to be in create mode
            return
        }
        splitView.removeArrangedSubview(optionsView)
        optionsView.isHidden = true
    }

    func showOptions(share: NKShare) {
        guard let kit = shareDataSource.kit else { return }
        optionsView.kit = kit
        optionsView.controller = ShareController(share: share, kit: kit)
        if !splitView.arrangedSubviews.contains(optionsView) {
            splitView.addArrangedSubview(optionsView)
            optionsView.isHidden = false
        }
    }

    func showError(_ errorString: String) {
        errorMessageStackView.isHidden = false
        errorTextLabel.stringValue = errorString
    }
}
