import { CheckboxTree, loadManifest } from '../vendor/checkbox-tree/checkbox-tree.js';
import { toTreeNodes } from './product-tree-model.js';

const STORAGE_KEY = 'tree-state-v2';
let productTree = null;

export function initializeTree(containerId, onSelectionChange) {
    const container = document.getElementById(containerId);
    if (!container) {
        throw new Error(`Tree container not found: ${containerId}`);
    }

    productTree?.destroy();
    productTree = new CheckboxTree(container, {
        storageKey: STORAGE_KEY,
        initiallyCollapsed: true,
        stateEncoding: 'tiered',
        onSelectionChange: event => {
            onSelectionChange(event);
            pushTreeState();
        }
    });

    container.addEventListener('click', event => {
        if (event.target.closest('.checkbox-tree__toggle')) {
            pushTreeState();
        }
    });
    window.addEventListener('popstate', () => {
        productTree?.restoreStateFromLocation();
        onSelectionChange({
            selectedIds: productTree?.getSelectedIds() ?? [],
            selectedNodes: productTree?.getSelectedNodes() ?? []
        });
    });
}

export async function renderProductTree(productsData) {
    const manifestSource = await fetch('data/product-tree-manifest.json').then(response => response.json());
    requireTree().options.manifest = loadManifest(manifestSource);
    requireTree().setData(toTreeNodes(productsData));
}

export function getCheckedPaths() {
    return productTree ? productTree.getSelectedNodes().map(node => node.metadata.path) : [];
}

export function getCheckedFileRefs() {
    const addressedFiles = [];
    const knownFiles = [];

    productTree?.getSelectedNodes().forEach(node => {
        const { path, addressed = [], known = [] } = node.metadata;
        const productKey = path[0];
        addressed.forEach(fileName => addressedFiles.push({ productKey, fileName }));
        known.forEach(fileName => knownFiles.push({ productKey, fileName }));
    });

    return {
        addressedFiles: dedupeFileRefs(addressedFiles),
        knownFiles: dedupeFileRefs(knownFiles)
    };
}

export function selectAddressedRelease(product, release) {
    return selectIssueRelease(product, release, 'addressed');
}

export function selectIssueRelease(product, release, issueType = 'addressed') {
    if (!productTree || !product || !release) return false;
    const node = findIssueReleaseNode(productTree.nodesById.values(), product, release, issueType);
    if (!node) return false;

    productTree.setSelectedIds([node.id]);
    pushTreeState();
    return true;
}

export function findAddressedReleaseNode(nodes, product, release) {
    return findIssueReleaseNode(nodes, product, release, 'addressed');
}

export function findIssueReleaseNode(nodes, product, release, issueType = 'addressed') {
    if (issueType !== 'addressed' && issueType !== 'known') return undefined;
    const filename = `${release}.md`;
    return Array.from(nodes).find(candidate =>
        candidate.metadata?.path?.[0] === product &&
        candidate.metadata?.[issueType]?.includes(filename)
    );
}

// State restoration now happens when setData renders the generic tree.
export function restoreTreeState() {}

function requireTree() {
    if (!productTree) {
        throw new Error('Tree is not initialized. Call initializeTree first.');
    }
    return productTree;
}

function pushTreeState() {
    if (!productTree?.options.manifest) {
        return;
    }
    const url = new URL(window.location.href);
    url.hash = productTree.serializeStateToFragment();
    if (url.href !== window.location.href) {
        history.pushState(null, '', url);
    }
}

function dedupeFileRefs(fileRefs) {
    const seen = new Set();
    return fileRefs.filter(({ productKey, fileName }) => {
        if (!productKey || !fileName) {
            return false;
        }
        const key = `${productKey}::${fileName}`;
        if (seen.has(key)) {
            return false;
        }
        seen.add(key);
        return true;
    });
}
