//
//  StatusActionAccountListViewController.swift
//  Tusker
//
//  Created by Shadowfacts on 1/17/23.
//  Copyright © 2023 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm

class StatusActionAccountListViewController: UIViewController {
    
    private let mastodonController: MastodonController
    private let actionType: StatusActionAccountListViewController.ActionType
    private let statusID: String
    private let statusState: CollapseState
    private var accountIDs: [String]?
    
    /// If `true`, a warning will be shown below the account list describing that the total favs/reblogs may be innacurate.
    var showInaccurateCountWarning = false
    
    private var state: State = .unloaded {
        didSet {
            switch oldValue {
            case .loading(let indicator):
                indicator.removeFromSuperview()
            case .displaying(let vc):
                vc.removeViewAndController()
            default:
                break
            }
            
            switch state {
            case .unloaded:
                break
            case .loading(let indicator):
                indicator.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(indicator)
                NSLayoutConstraint.activate([
                    indicator.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
                    indicator.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
                ])
            case .displaying(let vc):
                vc.view.translatesAutoresizingMaskIntoConstraints = false
                embedChild(vc)
            case .notFound:
                showStatusNotFound()
            }
        }
    }
    
    /**
     Creates a new view controller showing the accounts that performed the given action on the given status.
     
     - Parameter actionType The action that this VC is for.
     - Parameter statusID The ID of the status to show.
     - Parameter accountIDs The accounts that will be shown. If `nil` is passed, a request will be performed to load the accounts.
     - Parameter mastodonController The `MastodonController` instance this view controller uses.
     */
    init(actionType: StatusActionAccountListViewController.ActionType, statusID: String, statusState: CollapseState, accountIDs: [String]?, mastodonController: MastodonController) {
        self.mastodonController = mastodonController
        self.actionType = actionType
        self.statusID = statusID
        self.statusState = statusState
        self.accountIDs = accountIDs?.uniques()

        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        switch actionType {
        case .favorite:
            title = NSLocalizedString("Favorited By", comment: "status favorited by accounts list title")
        case .reblog:
            title = NSLocalizedString("Reblogged By", comment: "status reblogged by accounts list title")
        }

        view.backgroundColor = .appBackground
        
        NotificationCenter.default.addObserver(self, selector: #selector(handleStatusDeleted), name: .statusDeleted, object: mastodonController)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        if case .unloaded = state {
            Task {
                await loadStatus()
            }
        }
    }
    
    @objc private func handleStatusDeleted(_ notification: Foundation.Notification) {
        guard let userInfo = notification.userInfo,
              let statusIDs = userInfo["statusIDs"] as? [String] else {
            return
        }
        if statusIDs.contains(statusID) {
            state = .notFound
        }
    }
    
    // MARK: Loading
    
    private func loadStatus() async {
        @MainActor
        func doLoadStatus() async -> StatusMO? {
            switch await FetchStatusService(statusID: statusID, mastodonController: mastodonController).run() {
            case .loaded(let status):
                return mastodonController.persistentContainer.addOrUpdateOnViewContext(status: status)
            case .notFound:
                state = .notFound
                return nil
            case .error(let error):
                self.showStatusError(error)
                return nil
            }
        }

        if let cached = mastodonController.persistentContainer.status(for: statusID) {
            await statusLoaded(cached)
        } else {
            let indicator = UIActivityIndicatorView(style: .medium)
            indicator.startAnimating()
            state = .loading(indicator)

            if let status = await doLoadStatus() {
                await statusLoaded(status)
            }
        }
    }
    
    private func statusLoaded(_ status: StatusMO) async {
        let vc = StatusActionAccountListCollectionViewController(statusID: statusID, actionType: actionType, mastodonController: mastodonController)
        vc.addStatus(status, state: statusState, showInaccurateCountWarning: showInaccurateCountWarning)
        if let accountIDs {
            vc.setAccounts(accountIDs, animated: false)
        }
        state = .displaying(vc)
    }
    
    private func showStatusNotFound() {
        let notFoundView = StatusNotFoundView(frame: .zero)
        notFoundView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(notFoundView)
        NSLayoutConstraint.activate([
            notFoundView.leadingAnchor.constraint(equalToSystemSpacingAfter: view.safeAreaLayoutGuide.leadingAnchor, multiplier: 1),
            view.safeAreaLayoutGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: notFoundView.trailingAnchor, multiplier: 1),
            notFoundView.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor),
        ])
    }
    
    private func showStatusError(_ error: Client.Error) {
        let config = ToastConfiguration(from: error, with: "Error Loading Status", in: self) { [weak self] toast in
            toast.dismissToast(animated: true)
            await self?.loadStatus()
        }
        self.showToast(configuration: config, animated: true)
    }

}

extension StatusActionAccountListViewController {
    enum State {
        case unloaded
        case loading(UIActivityIndicatorView)
        case displaying(StatusActionAccountListCollectionViewController)
        case notFound
    }
}

extension StatusActionAccountListViewController {
    enum ActionType {
        case favorite, reblog
    }
}

extension StatusActionAccountListViewController: ToastableViewController {
    var toastScrollView: UIScrollView? {
        if case .displaying(let vc) = state {
            return vc.toastScrollView
        } else {
            return nil
        }
    }
}

extension StatusActionAccountListViewController: TuskerNavigationDelegate {
    nonisolated var apiController: MastodonController! { mastodonController }
}
