import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { GitMerge, Plus, Search } from 'lucide-react';
import { dataAPI } from '../infrastructure/api/data';
import { SYSTEM_TABLE_NAMES } from '@nexuscrm/generated-schema';
import { ConfirmationModal } from '@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui';
import { useErrorToast, useSuccessToast } from '@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui';
import { formatApiError } from '@nexuscrm/host-sdk';
import { RecordListSkeleton } from '@nexuscrm/salesforce-crm-standard-frontend/platform/components/ui';
import type { UserRole } from '../types';

import { RoleFormModal } from '../features/admin/components/roles/RoleFormModal';
import { RoleTreeNode, RoleTreeNodeData } from '../features/admin/components/roles/RoleTreeNode';

// ============================================================================
// Descendant collector — prevents circular parent assignment
// ============================================================================

function collectDescendantIds(nodeId: string, tree: RoleTreeNodeData[]): Set<string> {
    const ids = new Set<string>();
    const walk = (nodes: RoleTreeNodeData[]) => {
        for (const n of nodes) {
            if (n.Id === nodeId || ids.has(n.Id)) {
                ids.add(n.Id);
                const addAll = (children: RoleTreeNodeData[]) => {
                    for (const c of children) {
                        ids.add(c.Id);
                        addAll(c.children);
                    }
                };
                addAll(n.children);
            } else {
                walk(n.children);
            }
        }
    };
    walk(tree);
    return ids;
}

// ============================================================================
// Tree Builder — flat list → forest of RoleTreeNodeData
// ============================================================================

function buildTree(
    roles: UserRole[],
    userCountMap: Map<string, number>,
): RoleTreeNodeData[] {
    const nodeMap = new Map<string, RoleTreeNodeData>();

    // 1. Create all nodes with direct user counts
    for (const r of roles) {
        nodeMap.set(r.Id, {
            Id: r.Id,
            Name: r.Name,
            ParentRoleId: r.ParentRoleId ?? undefined,
            children: [],
            userCount: userCountMap.get(r.Id) ?? 0,
        });
    }

    // 2. Wire parent-child
    const roots: RoleTreeNodeData[] = [];
    for (const node of nodeMap.values()) {
        if (node.ParentRoleId && nodeMap.has(node.ParentRoleId)) {
            nodeMap.get(node.ParentRoleId)!.children.push(node);
        } else {
            roots.push(node);
        }
    }

    // 3. Sort children alphabetically at every level
    const sortChildren = (n: RoleTreeNodeData) => {
        n.children.sort((a, b) => a.Name.localeCompare(b.Name));
        n.children.forEach(sortChildren);
    };
    roots.sort((a, b) => a.Name.localeCompare(b.Name));
    roots.forEach(sortChildren);

    // 4. Bottom-up aggregation: each node's count = own users + all descendant users
    const aggregate = (node: RoleTreeNodeData): number => {
        let subtotal = node.userCount ?? 0;
        for (const child of node.children) {
            subtotal += aggregate(child);
        }
        node.userCount = subtotal;
        return subtotal;
    };
    roots.forEach(aggregate);

    return roots;
}

// ============================================================================
// Search helper — returns IDs of nodes matching + their ancestors
// ============================================================================

function getMatchingIds(roots: RoleTreeNodeData[], query: string): Set<string> {
    const ids = new Set<string>();
    const lq = query.toLowerCase();

    const walk = (node: RoleTreeNodeData): boolean => {
        const selfMatch = node.Name.toLowerCase().includes(lq);
        let childMatch = false;
        for (const child of node.children) {
            if (walk(child)) childMatch = true;
        }
        if (selfMatch || childMatch) {
            ids.add(node.Id);
            return true;
        }
        return false;
    };

    roots.forEach(walk);
    return ids;
}

// ============================================================================
// Collect all IDs in a tree (for expand-all)
// ============================================================================

function collectAllIds(roots: RoleTreeNodeData[]): Set<string> {
    const ids = new Set<string>();
    const walk = (n: RoleTreeNodeData) => {
        if (n.children.length > 0) {
            ids.add(n.Id);
            n.children.forEach(walk);
        }
    };
    roots.forEach(walk);
    return ids;
}

// ============================================================================
// RoleManager Page
// ============================================================================

export const RoleManager: React.FC = () => {
    const errorToast = useErrorToast();
    const successToast = useSuccessToast();

    // Data
    const [roles, setRoles] = useState<UserRole[]>([]);
    const [userCountMap, setUserCountMap] = useState<Map<string, number>>(new Map());
    const [loading, setLoading] = useState(true);

    // UI State
    const [searchQuery, setSearchQuery] = useState('');
    const [expandedIds, setExpandedIds] = useState<Set<string>>(new Set());
    const [selectedId, setSelectedId] = useState<string | null>(null);

    // Modals
    const [showCreateModal, setShowCreateModal] = useState(false);
    const [createParentId, setCreateParentId] = useState<string | null>(null);
    const [editingRole, setEditingRole] = useState<RoleTreeNodeData | null>(null);
    const [deletingRole, setDeletingRole] = useState<RoleTreeNodeData | null>(null);

    // ── Data Loading ──

    const loadRoles = useCallback(async () => {
        setLoading(true);
        try {
            const roleRecords = await dataAPI.query<UserRole>({
                objectApiName: SYSTEM_TABLE_NAMES.USERROLE,
                selectFields: ['Id', 'Name', 'ParentRoleId'],
            });
            setRoles(roleRecords);

            // User counts are best-effort — don't block role tree on this
            try {
                const users = await dataAPI.query<{ UserRoleId: string }>({
                    objectApiName: SYSTEM_TABLE_NAMES.USER,
                    selectFields: ['UserRoleId'],
                    filterExpr: 'UserRoleId != null',
                });
                const counts = new Map<string, number>();
                for (const u of users) {
                    if (u.UserRoleId) {
                        counts.set(u.UserRoleId, (counts.get(u.UserRoleId) ?? 0) + 1);
                    }
                }
                setUserCountMap(counts);
            } catch {
                // Silently ignore — roles still display without user counts
            }
        } catch (err: unknown) {
            errorToast(`Failed to load roles: ${formatApiError(err).message}`);
        } finally {
            setLoading(false);
        }
    }, [errorToast]);

    useEffect(() => {
        loadRoles();
    }, [loadRoles]);

    // ── Derived ──

    const tree = useMemo(() => buildTree(roles, userCountMap), [roles, userCountMap]);

    // Auto-expand all on first load
    useEffect(() => {
        if (tree.length > 0 && expandedIds.size === 0) {
            setExpandedIds(collectAllIds(tree));
        }
    }, [tree]); // eslint-disable-line react-hooks/exhaustive-deps

    const matchingIds = useMemo(
        () => (searchQuery.trim() ? getMatchingIds(tree, searchQuery.trim()) : null),
        [tree, searchQuery],
    );

    // When searching, auto-expand matching paths
    useEffect(() => {
        if (matchingIds) {
            setExpandedIds(prev => new Set([...prev, ...matchingIds]));
        }
    }, [matchingIds]);

    // ── Handlers ──

    const handleToggle = useCallback((id: string) => {
        setExpandedIds(prev => {
            const next = new Set(prev);
            if (next.has(id)) next.delete(id);
            else next.add(id);
            return next;
        });
    }, []);

    const handleCreate = async (data: { name: string; parentRoleId: string | null }) => {
        try {
            const payload: Record<string, unknown> = {
                Name: data.name,
            };
            if (data.parentRoleId) payload.ParentRoleId = data.parentRoleId;

            await dataAPI.createRecord(SYSTEM_TABLE_NAMES.USERROLE, payload);
            successToast('Role created successfully');
            setShowCreateModal(false);
            setCreateParentId(null);
            loadRoles();
        } catch (err: unknown) {
            errorToast(`Failed to create role: ${formatApiError(err).message}`);
        }
    };

    const handleUpdate = async (data: { name: string; parentRoleId: string | null }) => {
        if (!editingRole) return;
        try {
            const payload: Record<string, unknown> = {
                Name: data.name,
                ParentRoleId: data.parentRoleId ?? null,
            };
            await dataAPI.updateRecord(SYSTEM_TABLE_NAMES.USERROLE, editingRole.Id, payload);
            successToast('Role updated successfully');
            setEditingRole(null);
            loadRoles();
        } catch (err: unknown) {
            errorToast(`Failed to update role: ${formatApiError(err).message}`);
        }
    };

    const handleDelete = async () => {
        if (!deletingRole) return;
        try {
            await dataAPI.deleteRecord(SYSTEM_TABLE_NAMES.USERROLE, deletingRole.Id);
            successToast('Role deleted successfully');
            setDeletingRole(null);
            if (selectedId === deletingRole.Id) setSelectedId(null);
            loadRoles();
        } catch (err: unknown) {
            errorToast(`Failed to delete role: ${formatApiError(err).message}`);
        }
    };

    const handleAddChild = (parentId: string) => {
        setCreateParentId(parentId);
        setShowCreateModal(true);
    };

    // ── Helpers for modals ──

    // Exclude self + all descendants from parent candidates to prevent circular hierarchy
    const validParentRoles = useMemo(() => {
        if (!editingRole) return roles.map(r => ({ Id: r.Id, Name: r.Name }));
        const excluded = collectDescendantIds(editingRole.Id, tree);
        return roles
            .filter(r => !excluded.has(r.Id))
            .map(r => ({ Id: r.Id, Name: r.Name }));
    }, [roles, editingRole, tree]);

    const isEditing = !!editingRole;
    const formModalOpen = showCreateModal || isEditing;
    const formInitialData = isEditing
        ? { name: editingRole!.Name, parentRoleId: editingRole!.ParentRoleId ?? '' }
        : createParentId
            ? { name: '', parentRoleId: createParentId }
            : null;

    const totalRoles = roles.length;
    const rootRoles = tree.length;

    // ── Render ──

    return (
        <div className="max-w-5xl mx-auto p-6">
            {/* Header */}
            <div className="flex items-center justify-between mb-8">
                <div className="flex items-center gap-4">
                    <div className="p-3 bg-indigo-100 rounded-lg">
                        <GitMerge className="text-indigo-600" size={32} />
                    </div>
                    <div>
                        <h1 className="text-2xl font-bold text-slate-800">Role Hierarchy</h1>
                        <p className="text-slate-500">
                            Manage your organization's role tree for data access control.
                            {!loading && (
                                <span className="ml-1 text-slate-400">
                                    ({totalRoles} role{totalRoles !== 1 ? 's' : ''}, {rootRoles} root{rootRoles !== 1 ? 's' : ''})
                                </span>
                            )}
                        </p>
                    </div>
                </div>
                <button
                    onClick={() => { setCreateParentId(null); setShowCreateModal(true); }}
                    className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 font-medium shadow-sm transition-colors"
                >
                    <Plus size={18} />
                    New Role
                </button>
            </div>

            {/* Search */}
            <div className="mb-4">
                <div className="relative">
                    <Search size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
                    <input
                        type="text"
                        placeholder="Search roles..."
                        value={searchQuery}
                        onChange={e => setSearchQuery(e.target.value)}
                        className="w-full pl-10 pr-4 py-2 border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm"
                    />
                </div>
            </div>

            {/* Tree */}
            <div className="bg-white rounded-lg border border-slate-200 shadow-sm overflow-hidden">
                {loading ? (
                    <div className="p-6">
                        <RecordListSkeleton rows={6} columns={1} />
                    </div>
                ) : tree.length === 0 ? (
                    <div className="text-center py-12 text-slate-500">
                        <GitMerge size={40} className="mx-auto mb-3 text-slate-300" />
                        <p className="font-medium">No roles defined yet</p>
                        <p className="text-sm mt-1">Create your first role to build the organization hierarchy.</p>
                    </div>
                ) : matchingIds && matchingIds.size === 0 ? (
                    <div className="text-center py-8 text-slate-500">
                        No roles match "{searchQuery}"
                    </div>
                ) : (
                    <div className="p-3 space-y-0.5">
                        {tree.map(root => (
                            <RoleTreeNode
                                key={root.Id}
                                node={root}
                                level={0}
                                expandedIds={expandedIds}
                                selectedId={selectedId}
                                matchingIds={matchingIds}
                                onToggle={handleToggle}
                                onSelect={setSelectedId}
                                onEdit={setEditingRole}
                                onDelete={setDeletingRole}
                                onAddChild={handleAddChild}
                            />
                        ))}
                    </div>
                )}
            </div>

            {/* Form Modal (Create / Edit) */}
            <RoleFormModal
                isOpen={formModalOpen}
                initialData={formInitialData}
                isEditing={isEditing}
                allRoles={validParentRoles}
                onClose={() => {
                    setShowCreateModal(false);
                    setEditingRole(null);
                    setCreateParentId(null);
                }}
                onSave={isEditing ? handleUpdate : handleCreate}
            />

            {/* Delete Confirmation */}
            <ConfirmationModal
                isOpen={!!deletingRole}
                onClose={() => setDeletingRole(null)}
                onConfirm={handleDelete}
                title="Delete Role"
                message={
                    deletingRole?.children.length
                        ? `Are you sure you want to delete "${deletingRole.Name}"? Its ${deletingRole.children.length} child role(s) will become top-level.`
                        : `Are you sure you want to delete "${deletingRole?.Name}"?`
                }
                confirmLabel="Delete"
                variant="danger"
            />
        </div>
    );
};
