import React, { useState, useMemo } from 'react';
import { PageLayout, SObject, SObjectDescribe, RecordAccess, SObjectField } from '../../types';
import { hostApps } from '@nexuscrm/host-sdk';
import { ChevronDown, ChevronRight, Info } from 'lucide-react';
import { Tooltip } from '../ui/Tooltip';
import { evaluateExpression } from '../../core/utils/expressionEvaluator';
import { parseDotNotation, isEditableExpansion, buildExpansionLabel, toHydratedKey } from '../../infrastructure/api/data/lookupExpansion';
import { COMMON_FIELDS } from '../../core/constants';
import { Logger } from '../../core/services/Logger';
import { canReadField, canWriteField } from '../../core/metadata/describeCapabilities';
import type { MutationValidationFailure } from '../../features/record/components/inlineEditErrorMessage';


// System sections that should be collapsed by default (Salesforce pattern)
const SYSTEM_SECTION_LABELS = ['system information', 'system info', 'audit information'];

interface LayoutRendererProps {
    layout: PageLayout;
    record: SObject;
    objectMetadata: SObjectDescribe;
    onUpdate: (field: string, value: unknown) => Promise<void>;
    isReadOnly?: boolean;
    onNavigate?: (obj: string, id: string) => void;
    recordAccess?: RecordAccess;
    fieldSurfacePolicyScope?: string;
}

interface LayoutInlineEditFieldProps {
    objectApiName: string;
    recordId: string;
    field: SObjectField;
    value: unknown;
    record: SObject;
    objectFields?: SObjectField[];
    policyScope?: string;
    onUpdate?: (fieldName: string, newValue: unknown) => void;
    isEditable?: boolean;
    onNavigate?: (obj: string, id: string) => void;
    onValidationFailures?: (failures: MutationValidationFailure[]) => void;
    inputId?: string;
    ariaInvalid?: boolean;
    ariaDescribedBy?: string;
}

interface LayoutFieldItemResolverInput {
    layoutItem: unknown;
    fieldComponents: readonly unknown[];
    record: SObject;
    objectFields?: SObjectField[];
}

interface LayoutFieldItemResolution {
    permissionField: SObjectField;
    renderField?: SObjectField;
    label?: string;
    value: unknown;
}

type LayoutFieldItemResolver = (input: LayoutFieldItemResolverInput) => LayoutFieldItemResolution | null | undefined;

const ReadOnlyFieldValue: React.FC<{ value: unknown }> = ({ value }) => (
    <span
        className="block min-w-0 truncate text-sm text-slate-700"
        title={value != null ? String(value) : undefined}
    >
        {value != null ? String(value) : '—'}
    </span>
);

const inlineFieldAnchorId = (fieldName: string) => `inline-edit-field-${encodeURIComponent(fieldName)}`;
const inlineFieldControlId = (fieldName: string) => `${inlineFieldAnchorId(fieldName)}-control`;
const inlineFieldErrorId = (fieldName: string) => `${inlineFieldControlId(fieldName)}-error`;

/**
 * Detail-page field label. Renders the label + required marker and, when the
 * field carries `inlineHelpText`, a focusable ⓘ help affordance that shows the
 * text on hover (`title`) and to assistive tech (`aria-label`) — matching the
 * Salesforce read-only detail page. Shared by the layout field render paths.
 */
const DetailFieldLabel: React.FC<{
    label: string;
    required?: boolean;
    helpText?: string;
}> = ({ label, required, helpText }) => (
    <div className="flex items-center gap-1 text-xs text-gray-500 font-medium mb-1.5">
        <span>{label}</span>
        {required && <span className="text-red-500">*</span>}
        {helpText && (
            <Tooltip content={helpText} position="top" delay={100} usePortal>
                <button
                    type="button"
                    className="inline-flex text-gray-400 hover:text-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 rounded cursor-help"
                    aria-label={`Help: ${helpText}`}
                >
                    <Info size={13} aria-hidden="true" />
                </button>
            </Tooltip>
        )}
    </div>
);

export const LayoutRenderer: React.FC<LayoutRendererProps> = ({
    layout,
    record,
    objectMetadata,
    onUpdate,
    isReadOnly = false,
    onNavigate,
    recordAccess,
    fieldSurfacePolicyScope,
}) => {
    const InlineEditField = hostApps.apps.get(fieldSurfacePolicyScope)
        ?.shellComponents?.layoutInlineEditField as React.ComponentType<LayoutInlineEditFieldProps> | undefined;
    const layoutFieldItemResolver = hostApps.apps.get(fieldSurfacePolicyScope)
        ?.shellComponents?.layoutFieldItemResolver as LayoutFieldItemResolver | undefined;

    // Collapsed sections state - persists user's toggle actions
    const [collapsedSections, setCollapsedSections] = useState<Record<string, boolean>>({});
    const [validationFailures, setValidationFailures] = useState<MutationValidationFailure[]>([]);
    const [pendingValidationField, setPendingValidationField] = useState<string | null>(null);

    const layoutSectionSignature = (layout?.sections || []).map(section => {
        const label = section.heading || section.label || '';
        return `${section.id}\u0000${label}`;
    }).join('\u0001');

    const validationFieldTargets = useMemo(() => {
        const targets = new Map<string, { fieldName: string; sectionId: string }>();
        const registerTarget = (
            aliases: Array<string | undefined>,
            fieldName: string,
            sectionId: string,
        ) => {
            const target = { fieldName, sectionId };
            for (const alias of aliases) {
                const normalized = alias?.trim();
                if (normalized) targets.set(normalized.toLowerCase(), target);
            }
        };
        for (const section of layout?.sections || []) {
            if (section.visibilityCondition && !evaluateExpression(section.visibilityCondition, record)) {
                continue;
            }
            for (const row of section.layoutRows || []) {
                for (const item of row.layoutItems || []) {
                    if (item.placeholder) continue;
                    const fieldComponents = item.layoutComponents?.filter(
                        component => component.componentType === 'Field' || component.type === 'Field',
                    ) || [];
                    const resolved = layoutFieldItemResolver?.({
                        layoutItem: item,
                        fieldComponents,
                        record,
                        objectFields: objectMetadata.fields,
                    });
                    if (resolved) {
                        if (canReadField(resolved.permissionField)) {
                            const renderField = resolved.renderField ?? resolved.permissionField;
                            registerTarget([
                                renderField.name,
                                resolved.permissionField.name,
                                ...fieldComponents.map(component => component.apiName || component.value),
                            ], renderField.name, section.id);
                        }
                        continue;
                    }

                    const fieldComponent = fieldComponents[0];
                    if (!fieldComponent) continue;
                    const authoredName = fieldComponent.apiName || fieldComponent.value || '';
                    const expansion = parseDotNotation(authoredName);
                    const isLookupExpansion = fieldComponent.details?.lookupExpansion === true || !!expansion;
                    if (isLookupExpansion && expansion) {
                        const parentField = objectMetadata.fields?.find(
                            field => field.name === expansion.parentField,
                        );
                        if (
                            parentField
                            && canReadField(parentField)
                            && isEditableExpansion(expansion.childField)
                            && fieldComponent.details?.editable === true
                        ) {
                            registerTarget([parentField.name], parentField.name, section.id);
                        }
                        continue;
                    }

                    const field = objectMetadata.fields?.find(
                        candidate => candidate.name.toLowerCase() === authoredName.toLowerCase(),
                    );
                    if (field && canReadField(field)) {
                        registerTarget([field.name], field.name, section.id);
                    }
                }
            }
        }
        return targets;
    }, [layout?.sections, layoutFieldItemResolver, objectMetadata.fields, record]);

    const renderInlineField = (props: LayoutInlineEditFieldProps) => (
        InlineEditField
            ? <InlineEditField {...props} onValidationFailures={setValidationFailures} />
            : <ReadOnlyFieldValue value={props.value} />
    );

    const validationFailuresFor = (fieldName: string) => validationFailures.filter((failure) => {
        if (!failure.field) return false;
        const target = validationFieldTargets.get(failure.field.toLowerCase());
        return (target?.fieldName ?? failure.field).toLowerCase() === fieldName.toLowerCase();
    });

    const renderFieldValidationMessages = (fieldName: string) => {
        const failures = validationFailuresFor(fieldName);
        if (failures.length === 0) return null;
        return (
            <ul id={inlineFieldErrorId(fieldName)} className="mt-1 space-y-1 text-xs text-red-700">
                {failures.map((failure, index) => (
                    <li key={`${failure.field}-${index}-${failure.message}`}>{failure.message}</li>
                ))}
            </ul>
        );
    };

    const focusValidationTarget = React.useCallback((fieldName: string) => {
        const target = document.getElementById(inlineFieldAnchorId(fieldName));
        target?.scrollIntoView({ behavior: 'smooth', block: 'center' });
        const focusable = document.getElementById(inlineFieldControlId(fieldName))
            || target?.querySelector<HTMLElement>('input, textarea, select, button, [tabindex]');
        (focusable || target)?.focus();
    }, []);

    const focusValidationField = (fieldName: string) => {
        const target = validationFieldTargets.get(fieldName.toLowerCase());
        if (!target) return;
        if (collapsedSections[target.sectionId]) {
            setPendingValidationField(target.fieldName);
            setCollapsedSections(previous => ({ ...previous, [target.sectionId]: false }));
            return;
        }
        focusValidationTarget(target.fieldName);
    };

    // Preserve user toggles across equivalent layout objects while applying
    // the default only to genuinely new sections.
    React.useEffect(() => {
        setCollapsedSections(previous => {
            const next: Record<string, boolean> = {};
            for (const section of layout?.sections || []) {
                const label = section.heading || section.label;
                next[section.id] = previous[section.id]
                    ?? !!(label && SYSTEM_SECTION_LABELS.includes(label.toLowerCase()));
            }
            return next;
        });
    }, [layoutSectionSignature]);

    React.useEffect(() => {
        setValidationFailures([]);
        setPendingValidationField(null);
    }, [record[COMMON_FIELDS.ID]]);

    React.useEffect(() => {
        if (!pendingValidationField) return;
        const target = validationFieldTargets.get(pendingValidationField.toLowerCase());
        if (!target || collapsedSections[target.sectionId]) return;
        focusValidationTarget(target.fieldName);
        setPendingValidationField(null);
    }, [collapsedSections, focusValidationTarget, pendingValidationField, validationFieldTargets]);

    const toggleSection = (sectionId: string) => {
        setCollapsedSections(prev => ({
            ...prev,
            [sectionId]: !prev[sectionId]
        }));
    };

    // Length, not truthiness. The API client coerces a null sections key to an
    // empty array before anything sees it (decodeLayoutLabels), so this guard
    // could only ever have fired for a layout object built locally -- and the
    // projection now omits sections that have nothing left to show, which makes
    // "zero sections" an ordinary response rather than a malformed one. Without
    // this, a caller whose field-level security hides every field on the page
    // gets a blank card with no text and no log line.
    if (!layout?.sections?.length) {
        Logger.warn('[LayoutRenderer] No layout sections to render', { layoutId: layout?.id });
        return (
            <div className="text-center py-12 text-slate-500">
                No layout sections defined.
            </div>
        );
    }

    return (
        <div className="space-y-8">
            {validationFailures.length > 0 && (
                <div
                    className="bg-red-50 border border-red-200 rounded-lg p-4 scroll-mt-24"
                    role="alert"
                    tabIndex={-1}
                >
                    <p className="text-sm font-semibold text-red-800">
                        Save blocked by {validationFailures.length} validation {validationFailures.length === 1 ? 'error' : 'errors'}
                    </p>
                    <ul className="mt-2 space-y-1 text-sm text-red-700">
                        {validationFailures.map((failure, index) => {
                            const canNavigate = !!failure.field
                                && validationFieldTargets.has(failure.field.toLowerCase());
                            return (
                                <li key={`${failure.field || 'form'}-${index}-${failure.message}`}>
                                    {canNavigate ? (
                                        <button
                                            type="button"
                                            onClick={() => focusValidationField(failure.field!)}
                                            className="font-semibold underline underline-offset-2 hover:text-red-900"
                                        >
                                            {failure.label || failure.field}
                                        </button>
                                    ) : failure.field ? (
                                        <span className="font-semibold">{failure.label || failure.field}</span>
                                    ) : null}
                                    {failure.field ? ': ' : ''}{failure.message}
                                </li>
                            );
                        })}
                    </ul>
                </div>
            )}
            {layout.sections.map(section => {
                const isCollapsed = collapsedSections[section.id];
                const sectionLabel = section.heading || section.label || 'Untitled Section';

                // Visibility Check
                if (section.visibilityCondition) {
                    const shouldShow = evaluateExpression(section.visibilityCondition, record);
                    if (!shouldShow) return null;
                }

                return (
                    <div key={section.id} className="bg-white border rounded-lg shadow-sm">
                        {/* Section Header */}
                        {(section.useHeading !== false || section.label) && ( // Force heading if label exists (Salesforce parity)
                            <div
                                className="bg-slate-50 px-6 py-4 border-b border-slate-100 flex items-center gap-3 cursor-pointer hover:bg-slate-100 transition-colors select-none"
                                onClick={() => toggleSection(section.id)}
                            >
                                <button className="text-slate-400 p-1 hover:bg-slate-200 rounded-full transition-colors">
                                    {isCollapsed ? <ChevronRight size={18} /> : <ChevronDown size={18} />}
                                </button>
                                <h3 className="text-sm font-semibold text-slate-800 uppercase tracking-wide">
                                    {sectionLabel}
                                </h3>
                            </div>
                        )}

                        {/* Section Content */}
                        {!isCollapsed && (
                            <div className="p-6">
                                <div className="space-y-4">
                                    {section.layoutRows?.map((row, rowIndex) => (
                                        <div key={rowIndex} className={`grid gap-8 ${row.layoutItems.length > 1 ? 'grid-cols-1 md:grid-cols-2' : 'grid-cols-1'}`}>
                                            {row.layoutItems.map((item, itemIndex) => {
                                                // Handle empty/placeholder items
                                                if (item.placeholder) {
                                                    return <div key={itemIndex} className="hidden md:block" />;
                                                }

                                                const fieldComponents = item.layoutComponents?.filter(c => c.componentType === 'Field' || c.type === 'Field') || [];
                                                const resolvedLayoutFieldItem = layoutFieldItemResolver?.({
                                                    layoutItem: item,
                                                    fieldComponents,
                                                    record,
                                                    objectFields: objectMetadata.fields,
                                                });
                                                if (resolvedLayoutFieldItem) {
                                                    const permissionField = resolvedLayoutFieldItem.permissionField;
                                                    if (!canReadField(permissionField)) {
                                                        return null;
                                                    }
                                                    const renderField = resolvedLayoutFieldItem.renderField ?? permissionField;
                                                    const displayLabel = resolvedLayoutFieldItem.label ?? item.label ?? renderField.label;
                                                    const isCreating = !record[COMMON_FIELDS.ID]; // If no ID, we're creating
                                                    const canEdit = !isReadOnly &&
                                                        canWriteField(permissionField, isCreating ? 'create' : 'edit') &&
                                                        item.editableForUpdate !== false &&
                                                        (recordAccess?.hasEditAccess ?? true) &&
                                                        !recordAccess?.isLocked;

                                                    const fieldFailures = validationFailuresFor(renderField.name);
                                                    return (
                                                        <div
                                                            key={itemIndex}
                                                            id={inlineFieldAnchorId(renderField.name)}
                                                            tabIndex={-1}
                                                            className={`group min-w-0 rounded-md ${fieldFailures.length > 0 ? 'ring-2 ring-red-300 bg-red-50/40 p-2' : ''}`}
                                                        >
                                                            <DetailFieldLabel
                                                                label={displayLabel}
                                                                required={item.required}
                                                                helpText={renderField.inlineHelpText}
                                                            />
                                                            <div className="min-h-[30px] min-w-0 text-sm">
                                                                {renderInlineField({
                                                                    objectApiName: objectMetadata.name,
                                                                    recordId: record[COMMON_FIELDS.ID] as string,
                                                                    field: renderField,
                                                                    value: resolvedLayoutFieldItem.value,
                                                                    record,
                                                                    objectFields: objectMetadata.fields,
                                                                    onUpdate,
                                                                    isEditable: canEdit,
                                                                    onNavigate,
                                                                    policyScope: fieldSurfacePolicyScope,
                                                                    inputId: inlineFieldControlId(renderField.name),
                                                                    ariaInvalid: fieldFailures.length > 0,
                                                                    ariaDescribedBy: fieldFailures.length > 0 ? inlineFieldErrorId(renderField.name) : undefined,
                                                                })}
                                                            </div>
                                                            {renderFieldValidationMessages(renderField.name)}
                                                        </div>
                                                    );
                                                }

                                                // Find the field component
                                                const fieldComponent = fieldComponents[0] || null;

                                                // Handle EmptySpace explicitly
                                                const emptySpace = item.layoutComponents?.find(c => c.componentType === 'EmptySpace' || c.type === 'EmptySpace');
                                                if (emptySpace) {
                                                    return <div key={itemIndex} className="hidden md:block" />;
                                                }

                                                if (!fieldComponent) {
                                                    return null;
                                                }

                                                const fieldName = fieldComponent.apiName || fieldComponent.value || '';

                                                const expansion = parseDotNotation(fieldName);
                                                const isLookupExpansion = fieldComponent.details?.lookupExpansion === true || !!expansion;
                                                if (isLookupExpansion && expansion) {
                                                    const { parentField: parentFieldName, childField: childFieldName } = expansion;
                                                    const parentField = objectMetadata.fields?.find(f => f.name === parentFieldName);

                                                    if (!canReadField(parentField)) {
                                                        return null;
                                                    }

                                                    const displayValue = record[toHydratedKey(parentFieldName, childFieldName)] ??
                                                        (childFieldName === 'Id' ? record[parentFieldName] : undefined);
                                                    const displayLabel = item.label || buildExpansionLabel(parentField?.label || parentFieldName, childFieldName);
                                                    const editable = isEditableExpansion(childFieldName) &&
                                                        fieldComponent.details?.editable === true;

                                                    if (editable && parentField) {
                                                        const canEdit = !isReadOnly &&
                                                            canWriteField(parentField, 'edit') &&
                                                            item.editableForUpdate !== false &&
                                                            (recordAccess?.hasEditAccess ?? true) &&
                                                            !recordAccess?.isLocked;
                                                        const syntheticRefField = {
                                                            ...parentField,
                                                            name: parentFieldName,
                                                            label: displayLabel,
                                                            type: 'reference' as const,
                                                        };
                                                        const fieldFailures = validationFailuresFor(parentFieldName);
                                                        return (
                                                            <div
                                                                key={itemIndex}
                                                                id={inlineFieldAnchorId(parentFieldName)}
                                                                tabIndex={-1}
                                                                className={`group min-w-0 rounded-md ${fieldFailures.length > 0 ? 'ring-2 ring-red-300 bg-red-50/40 p-2' : ''}`}
                                                            >
                                                                <div className="text-xs text-gray-500 font-medium mb-1.5">
                                                                    {displayLabel}
                                                                </div>
                                                                <div className="min-h-[30px] min-w-0 text-sm">
                                                                    {renderInlineField({
                                                                        objectApiName: objectMetadata.name,
                                                                        recordId: record[COMMON_FIELDS.ID] as string,
                                                                        field: syntheticRefField,
                                                                        value: record[parentFieldName],
                                                                        record,
                                                                        objectFields: objectMetadata.fields,
                                                                        onUpdate,
                                                                        isEditable: canEdit,
                                                                        onNavigate,
                                                                        policyScope: fieldSurfacePolicyScope,
                                                                        inputId: inlineFieldControlId(parentFieldName),
                                                                        ariaInvalid: fieldFailures.length > 0,
                                                                        ariaDescribedBy: fieldFailures.length > 0 ? inlineFieldErrorId(parentFieldName) : undefined,
                                                                    })}
                                                                </div>
                                                                {renderFieldValidationMessages(parentFieldName)}
                                                            </div>
                                                        );
                                                    }

                                                    return (
                                                        <div key={itemIndex} className="group min-w-0">
                                                            <div className="text-xs text-gray-500 font-medium mb-1.5">
                                                                {displayLabel}
                                                            </div>
                                                            <div className="min-h-[30px] min-w-0 text-sm">
                                                                <span
                                                                    className="block min-w-0 truncate text-sm text-slate-700"
                                                                    title={displayValue != null ? String(displayValue) : undefined}
                                                                >
                                                                    {displayValue != null ? String(displayValue) : '—'}
                                                                </span>
                                                            </div>
                                                        </div>
                                                    );
                                                }

                                                // Case-insensitive lookup for metadata
                                                const field = objectMetadata.fields?.find(f => f.name.toLowerCase() === fieldName.toLowerCase());

                                                // Skip invalid fields (deleted or not in schema)
                                                if (!field) {
                                                    return null;
                                                }

                                                if (!canReadField(field)) {
                                                    return null;
                                                }

                                                // Check edit permission for this specific field
                                                // Layout Item might override edibility
                                                // Record-level access gates inline editing (falls back to true for backwards compat)
                                                const canEdit = !isReadOnly &&
                                                    canWriteField(field, 'edit') &&
                                                    item.editableForUpdate !== false &&
                                                    (recordAccess?.hasEditAccess ?? true) &&
                                                    !recordAccess?.isLocked;

                                                const fieldFailures = validationFailuresFor(field.name);
                                                return (
                                                    <div
                                                        key={itemIndex}
                                                        id={inlineFieldAnchorId(field.name)}
                                                        tabIndex={-1}
                                                        className={`group min-w-0 rounded-md ${fieldFailures.length > 0 ? 'ring-2 ring-red-300 bg-red-50/40 p-2' : ''}`}
                                                    >
                                                        <DetailFieldLabel
                                                            label={item.label || field.label}
                                                            required={item.required}
                                                            helpText={field.inlineHelpText}
                                                        />
                                                        <div className="min-h-[30px] min-w-0 text-sm">
                                                            {renderInlineField({
                                                                objectApiName: objectMetadata.name,
                                                                recordId: record[COMMON_FIELDS.ID] as string,
                                                                field,
                                                                value: record[fieldName] !== undefined ? record[fieldName] : record[field.name],
                                                                record,
                                                                objectFields: objectMetadata.fields,
                                                                onUpdate,
                                                                isEditable: canEdit,
                                                                onNavigate,
                                                                policyScope: fieldSurfacePolicyScope,
                                                                inputId: inlineFieldControlId(field.name),
                                                                ariaInvalid: fieldFailures.length > 0,
                                                                ariaDescribedBy: fieldFailures.length > 0 ? inlineFieldErrorId(field.name) : undefined,
                                                            })}
                                                        </div>
                                                        {renderFieldValidationMessages(field.name)}
                                                    </div>
                                                );
                                            })}
                                        </div>
                                    ))}


                                </div>
                            </div>
                        )}
                    </div>
                );
            })}
        </div>
    );
};
