import UIKit
import SwiftUI

/// Screen 2: Multi-select contact picker for group creation — Telegram parity.
/// Custom glass header: back chevron + "New Group" + counter + "Next" button.
/// Glass search bar, token chips, contact list with checkmarks.
final class GroupContactPickerViewController: UIViewController {

    // MARK: - Callbacks

    var onOpenChat: ((ChatRoute) -> Void)?

    // MARK: - Data

    private var allContacts: [Dialog] = []
    private var filteredContacts: [Dialog] = []
    private var selectedContacts: [Dialog] = []
    private var selectedKeys: Set<String> = []
    private var searchQuery = ""
    private let maxMembers = 200

    // MARK: - Header Views

    private let headerBarHeight: CGFloat = 44
    private let backButton = ComposeGlassBackButton()
    private let titleLabel = UILabel()
    private let counterLabel = UILabel()
    private let nextButton = ComposeGlassActionButton()
    private let glassSearchBar = ComposeGlassSearchBar()

    // MARK: - Token Views

    private let tokenScrollView = UIScrollView()
    private let tokenContainerView = UIView()
    private var tokenChips: [TokenChipView] = []
    private let tokenSeparator = UIView()
    private let tokenAreaMinHeight: CGFloat = 0
    private let tokenHSpacing: CGFloat = 6
    private let tokenVSpacing: CGFloat = 6
    private let tokenSideInset: CGFloat = 12

    // MARK: - Content

    private var collectionView: UICollectionView!
    private var observationTask: Task<Void, Never>?

    // MARK: - Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor { $0.userInterfaceStyle == .dark ? .black : .white }
        hidesBottomBarWhenPushed = true

        setupCustomHeader()
        setupGlassSearchBar()
        setupTokenArea()
        setupCollectionView()
        loadContacts()
        startObservationLoop()
        updateHeader()
    }

    deinit {
        observationTask?.cancel()
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        layoutCustomHeader()
        layoutTokens()
    }

    // MARK: - Custom Glass Header

    private func setupCustomHeader() {
        backButton.addTarget(self, action: #selector(backTapped), for: .touchUpInside)
        backButton.layer.zPosition = 55
        view.addSubview(backButton)

        titleLabel.text = "New Group"
        titleLabel.font = .systemFont(ofSize: 17, weight: .semibold)
        titleLabel.textColor = UIColor(RosettaColors.Adaptive.text)
        titleLabel.textAlignment = .center
        titleLabel.layer.zPosition = 55
        view.addSubview(titleLabel)

        counterLabel.font = .systemFont(ofSize: 12, weight: .regular)
        counterLabel.textColor = UIColor(RosettaColors.Adaptive.textSecondary)
        counterLabel.textAlignment = .center
        counterLabel.layer.zPosition = 55
        view.addSubview(counterLabel)

        nextButton.title = "Next"
        nextButton.isEnabled = true
        nextButton.addTarget(self, action: #selector(nextTapped), for: .touchUpInside)
        nextButton.layer.zPosition = 55
        view.addSubview(nextButton)
    }

    private func setupGlassSearchBar() {
        glassSearchBar.placeholder = "Who would you like to add?"
        glassSearchBar.onTextChanged = { [weak self] text in
            self?.searchQuery = text
            self?.applySearch()
        }
        glassSearchBar.layer.zPosition = 55
        view.addSubview(glassSearchBar)
    }

    private var headerContentBottom: CGFloat {
        view.safeAreaInsets.top + headerBarHeight + 8 + 44 + 8 // header + gap + search(44pt) + gap
    }

    private func layoutCustomHeader() {
        let safeTop = view.safeAreaInsets.top
        let centerY = safeTop + headerBarHeight * 0.5

        // Back button (left)
        let backSize = backButton.intrinsicContentSize
        backButton.frame = CGRect(
            x: 8,
            y: centerY - backSize.height * 0.5,
            width: backSize.width,
            height: backSize.height
        )

        // Next button (right)
        let nextSize = nextButton.intrinsicContentSize
        nextButton.frame = CGRect(
            x: view.bounds.width - 8 - nextSize.width,
            y: centerY - nextSize.height * 0.5,
            width: nextSize.width,
            height: nextSize.height
        )

        // Title + counter (center)
        let titleWidth: CGFloat = 200
        let titleX = (view.bounds.width - titleWidth) / 2
        titleLabel.frame = CGRect(x: titleX, y: centerY - 18, width: titleWidth, height: 20)
        counterLabel.frame = CGRect(x: titleX, y: centerY + 2, width: titleWidth, height: 16)

        // Search bar
        let searchY = safeTop + headerBarHeight + 8
        glassSearchBar.frame = CGRect(
            x: 16,
            y: searchY,
            width: view.bounds.width - 32,
            height: 44
        )
    }

    private func updateHeader() {
        counterLabel.text = "\(selectedContacts.count)/\(maxMembers)"
        nextButton.isEnabled = true
    }

    // MARK: - Token Area (manual frame layout — no Auto Layout conflicts)

    private func setupTokenArea() {
        tokenScrollView.showsVerticalScrollIndicator = false
        tokenScrollView.showsHorizontalScrollIndicator = false
        view.addSubview(tokenScrollView)
        tokenScrollView.addSubview(tokenContainerView)

        tokenSeparator.backgroundColor = UIColor { traits in
            traits.userInterfaceStyle == .dark
                ? UIColor.white.withAlphaComponent(0.08)
                : UIColor.black.withAlphaComponent(0.08)
        }
        view.addSubview(tokenSeparator)
    }

    // MARK: - Collection View

    private func setupCollectionView() {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(56))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)
        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(56))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [item])
        let section = NSCollectionLayoutSection(group: group)
        let layout = UICollectionViewCompositionalLayout(section: section)

        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.backgroundColor = .clear
        collectionView.delegate = self
        collectionView.dataSource = self
        collectionView.keyboardDismissMode = .onDrag
        collectionView.register(ComposeContactCell.self, forCellWithReuseIdentifier: ComposeContactCell.reuseID)
        view.addSubview(collectionView)

        // Header views must be above collection for hit-testing
        view.bringSubviewToFront(backButton)
        view.bringSubviewToFront(titleLabel)
        view.bringSubviewToFront(counterLabel)
        view.bringSubviewToFront(nextButton)
        view.bringSubviewToFront(glassSearchBar)
        view.bringSubviewToFront(tokenScrollView)
        view.bringSubviewToFront(tokenSeparator)
    }

    // MARK: - Data Loading

    private func loadContacts() {
        let dialogs = DialogRepository.shared.dialogs
        allContacts = dialogs.values
            .filter { !$0.isGroup && !$0.isSavedMessages && !SystemAccounts.isSystemAccount($0.opponentKey) }
            .sorted { $0.opponentTitle.localizedCaseInsensitiveCompare($1.opponentTitle) == .orderedAscending }
        applySearch()
    }

    private func applySearch() {
        if searchQuery.isEmpty {
            filteredContacts = allContacts
        } else {
            let query = searchQuery.lowercased()
            filteredContacts = allContacts.filter {
                $0.opponentTitle.lowercased().contains(query) ||
                $0.opponentUsername.lowercased().contains(query)
            }
        }
        collectionView?.reloadData()
    }

    private func startObservationLoop() {
        // dialogsVersion is @ObservationIgnored — track dialogs directly
        observationTask = Task { @MainActor [weak self] in
            while !Task.isCancelled {
                guard let self else { return }
                withObservationTracking {
                    _ = DialogRepository.shared.dialogs
                } onChange: { }
                try? await Task.sleep(for: .milliseconds(500))
                guard !Task.isCancelled else { return }
                self.loadContacts()
            }
        }
    }

    // MARK: - Selection

    private func toggleSelection(for dialog: Dialog) {
        if selectedKeys.contains(dialog.opponentKey) {
            deselectContact(key: dialog.opponentKey)
        } else {
            selectContact(dialog)
        }
    }

    private func selectContact(_ dialog: Dialog) {
        guard !selectedKeys.contains(dialog.opponentKey), selectedContacts.count < maxMembers else { return }

        selectedContacts.append(dialog)
        selectedKeys.insert(dialog.opponentKey)

        let chip = TokenChipView(dialog: dialog)
        chip.onRemove = { [weak self] key in self?.deselectContact(key: key) }
        tokenChips.append(chip)
        tokenContainerView.addSubview(chip)

        UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5) {
            self.layoutTokens()
            self.view.layoutIfNeeded()
        }

        updateHeader()
        reloadVisibleCells()

        // Clear search after selection
        glassSearchBar.textField.text = ""
        searchQuery = ""
        applySearch()
    }

    private func deselectContact(key: String) {
        selectedKeys.remove(key)
        selectedContacts.removeAll { $0.opponentKey == key }

        if let idx = tokenChips.firstIndex(where: { $0.dialogKey == key }) {
            let chip = tokenChips.remove(at: idx)
            UIView.animate(withDuration: 0.2, animations: {
                chip.alpha = 0
                chip.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
            }) { _ in chip.removeFromSuperview() }
        }

        UIView.animate(withDuration: 0.25, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0.5) {
            self.layoutTokens()
            self.view.layoutIfNeeded()
        }

        updateHeader()
        reloadVisibleCells()
    }

    private func reloadVisibleCells() {
        for indexPath in collectionView.indexPathsForVisibleItems {
            guard let cell = collectionView.cellForItem(at: indexPath) as? ComposeContactCell else { continue }
            let dialog = filteredContacts[indexPath.item]
            cell.updateCheckmark(isSelected: selectedKeys.contains(dialog.opponentKey))
        }
    }

    // MARK: - Token + Collection Layout (all manual frames)

    private func layoutTokens() {
        let containerWidth = view.bounds.width
        guard containerWidth > 0 else { return }

        let baseY = headerContentBottom
        var tokenAreaHeight: CGFloat = 0

        if !tokenChips.isEmpty {
            tokenSeparator.isHidden = false
            var x: CGFloat = tokenSideInset
            var y: CGFloat = tokenVSpacing
            let maxX = containerWidth - tokenSideInset
            let chipHeight: CGFloat = 28

            for chip in tokenChips {
                let chipWidth = chip.frame.width
                if x + chipWidth > maxX && x > tokenSideInset {
                    x = tokenSideInset
                    y += chipHeight + tokenVSpacing
                }
                chip.frame.origin = CGPoint(x: x, y: y)
                x += chipWidth + tokenHSpacing
            }

            let totalHeight = y + chipHeight + tokenVSpacing
            let maxHeight: CGFloat = 100
            let clampedHeight = min(totalHeight, maxHeight)

            tokenScrollView.frame = CGRect(x: 0, y: baseY, width: containerWidth, height: clampedHeight)
            tokenContainerView.frame = CGRect(origin: .zero, size: CGSize(width: containerWidth, height: totalHeight))
            tokenScrollView.contentSize = CGSize(width: containerWidth, height: totalHeight)

            if totalHeight > maxHeight {
                tokenScrollView.setContentOffset(CGPoint(x: 0, y: totalHeight - maxHeight), animated: true)
            }

            tokenAreaHeight = clampedHeight
        } else {
            tokenScrollView.frame = CGRect(x: 0, y: baseY, width: containerWidth, height: 0)
            tokenSeparator.isHidden = true
        }

        // Separator
        let separatorY = baseY + tokenAreaHeight
        tokenSeparator.frame = CGRect(x: 0, y: separatorY, width: containerWidth, height: 0.5)

        // Collection view fills remaining space
        let collectionY = separatorY + (tokenChips.isEmpty ? 0 : 0.5)
        collectionView.frame = CGRect(
            x: 0,
            y: collectionY,
            width: containerWidth,
            height: view.bounds.height - collectionY
        )
    }

    // MARK: - Actions

    @objc private func backTapped() {
        navigationController?.popViewController(animated: true)
    }

    @objc private func nextTapped() {
        let setupVC = GroupSetupViewController(selectedContacts: selectedContacts)
        setupVC.onOpenChat = onOpenChat
        navigationController?.pushViewController(setupVC, animated: true)
    }
}

// MARK: - UICollectionViewDataSource

extension GroupContactPickerViewController: UICollectionViewDataSource {

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        filteredContacts.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ComposeContactCell.reuseID, for: indexPath) as! ComposeContactCell
        let dialog = filteredContacts[indexPath.item]
        cell.configure(with: dialog, isSelected: selectedKeys.contains(dialog.opponentKey), showCheckmark: true)
        return cell
    }
}

// MARK: - UICollectionViewDelegate

extension GroupContactPickerViewController: UICollectionViewDelegate {

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        collectionView.deselectItem(at: indexPath, animated: true)
        toggleSelection(for: filteredContacts[indexPath.item])
    }
}
