'use client';

import React, { useEffect, useLayoutEffect, useMemo, useRef, useState, type ReactNode } from 'react';
import { createPortal } from 'react-dom';
import Link from 'next/link';
import { Tooltip } from 'antd';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { useMediaQuery } from '@/hooks/useMediaQuery';
import type {
    ModalOpcaoAction,
    ModalOpcaoGroup,
} from '@/components/ModalOpcoesBase/ModalOpcoesBase';
import {
    mergeDetailPrintIntoActions,
    mergeDetailPrintIntoGroups,
    useDetailPrint,
} from '@/components/layouts/DetailPrint';
import { cn } from '@/lib/utils/cn';
import {
    MEDIA_DETAIL_OPCOES_RAIL_PINNED,
    setDetailOpcoesOwnsTrigger,
} from './useDetailOpcoesDesktopRail';
import styles from './DetailOpcoesSidePanel.module.scss';

const DEFAULT_WIDTH = 280;
/** Espaço entre o rail (`DetailOpcoesSidePanel`) e o conteúdo da página. */
const CONTENT_GAP_PX = 15;

/**
 * Raízes de conteúdo onde reservar padding para o rail fixo.
 * - `[data-detail-opcoes-content]` — shell explícito (ex.: ProjetoLayout)
 * - `.ant-layout-content` — dashboard Ant Design
 * - `#main-content` — fallback (ProjetoLayout / páginas sem Ant Layout)
 */
function resolveDetailOpcoesContentElements(): HTMLElement[] {
    const marked = Array.from(
        document.querySelectorAll<HTMLElement>('[data-detail-opcoes-content]'),
    );
    if (marked.length > 0) {
        return marked;
    }
    const ant = Array.from(document.querySelectorAll<HTMLElement>('.ant-layout-content'));
    if (ant.length > 0) {
        return ant;
    }
    const main = document.querySelector<HTMLElement>('#main-content');
    return main ? [main] : [];
}

export interface DetailOpcoesSidePanelProps {
    /** Sempre visível enquanto montado (não é modal). */
    actions?: ModalOpcaoAction[];
    /** Seções (ex.: tarefa). Se definido, tem prioridade sobre `actions`. */
    groups?: ModalOpcaoGroup[];
    /**
     * Conteúdo extra no corpo do rail (ex.: filtros do relatório).
     * Renderizado acima da lista de ações.
     */
    children?: ReactNode;
    /**
     * Título da seção de `children` (padrão ReportFiltersSidePanel).
     * String vazia omite o título. @default "Filtros"
     */
    childrenTitle?: string;
    width?: number;
    /**
     * Lado do viewport onde o rail fica fixo.
     * @default 'right'
     */
    side?: 'left' | 'right';
    /**
     * Mutação em andamento: desativa todas as ações do rail
     * (paridade com `ModalOpcoesBase.loading`).
     */
    loading?: boolean;
    /**
     * Impressão da ficha de detalhe (`DetailPrintSheet`).
     * Por defeito usa `useDetailPrint` (imprime `[data-detail-print-sheet]`).
     */
    onPrint?: () => void;
    /**
     * Inclui sempre a ação "Imprimir" no rail (padrão Waygest em detalhe).
     * Desativar só em excepções documentadas.
     * @default true
     */
    showPrintAction?: boolean;
    /** Rótulo da ação Imprimir. @default "Imprimir" */
    printLabel?: string;
    'data-testid'?: string;
    'aria-label'?: string;
}

/**
 * Rail fixo permanente à direita em páginas de detalhe (desktop ≥1025px).
 * Padrão Waygest para opções de registro — listagens/mobile usam `ModalOpcoesBase`.
 *
 * @see useDetailOpcoesDesktopRail
 * @see DetailOpcoesPresentation
 */
export function DetailOpcoesSidePanel({
    actions = [],
    groups,
    children,
    childrenTitle = 'Filtros',
    width = DEFAULT_WIDTH,
    side = 'right',
    loading = false,
    onPrint,
    showPrintAction = true,
    printLabel = 'Imprimir',
    'data-testid': dataTestId,
    'aria-label': ariaLabel,
}: DetailOpcoesSidePanelProps) {
    const defaultPrint = useDetailPrint();
    const handlePrint = onPrint ?? defaultPrint;
    const isLeft = side === 'left';
    const pinned = useMediaQuery(MEDIA_DETAIL_OPCOES_RAIL_PINNED);
    const [overlayOpen, setOverlayOpen] = useState(false);
    const panelRef = useRef<HTMLElement>(null);

    useLayoutEffect(() => {
        setDetailOpcoesOwnsTrigger(true);
        return () => setDetailOpcoesOwnsTrigger(false);
    }, []);

    const resolvedActions = useMemo(
        () =>
            groups?.length
                ? undefined
                : mergeDetailPrintIntoActions(actions, {
                      onPrint: handlePrint,
                      label: printLabel,
                      enabled: showPrintAction,
                  }),
        [actions, groups, handlePrint, printLabel, showPrintAction],
    );

    const resolvedGroups = useMemo(
        () =>
            groups?.length
                ? mergeDetailPrintIntoGroups(groups, {
                      onPrint: handlePrint,
                      label: printLabel,
                      enabled: showPrintAction,
                  })
                : undefined,
        [groups, handlePrint, printLabel, showPrintAction],
    );

    useEffect(() => {
        if (!pinned) {
            return undefined;
        }
        const contentEls = resolveDetailOpcoesContentElements();
        const paddingProp = isLeft ? 'paddingLeft' : 'paddingRight';
        const oppositeCssProp = isLeft ? 'padding-right' : 'padding-left';
        const contentReservePx = width + CONTENT_GAP_PX;
        document.documentElement.classList.add('detail-opcoes-rail-open');
        document.documentElement.classList.toggle('detail-opcoes-rail-left', isLeft);
        document.documentElement.style.setProperty('--detail-opcoes-rail-width', `${width}px`);
        document.documentElement.style.setProperty(
            '--detail-opcoes-content-gap',
            `${CONTENT_GAP_PX}px`,
        );
        for (const contentEl of contentEls) {
            contentEl.style[paddingProp] = `${contentReservePx}px`;
            contentEl.style.removeProperty(oppositeCssProp);
            contentEl.style.boxSizing = 'border-box';
        }
        return () => {
            document.documentElement.classList.remove('detail-opcoes-rail-open');
            document.documentElement.classList.remove('detail-opcoes-rail-left');
            document.documentElement.style.removeProperty('--detail-opcoes-rail-width');
            document.documentElement.style.removeProperty('--detail-opcoes-content-gap');
            for (const contentEl of contentEls) {
                contentEl.style.removeProperty('padding-right');
                contentEl.style.removeProperty('padding-left');
            }
        };
    }, [pinned, width, isLeft]);

    useEffect(() => {
        if (pinned || !overlayOpen) {
            return undefined;
        }
        const onKey = (event: KeyboardEvent) => {
            if (event.key === 'Escape') {
                setOverlayOpen(false);
            }
        };
        window.addEventListener('keydown', onKey);
        panelRef.current?.focus();
        return () => window.removeEventListener('keydown', onKey);
    }, [overlayOpen, pinned]);

    if (typeof document === 'undefined') {
        return null;
    }

    const renderAction = (action: ModalOpcaoAction) => {
        // Navegação (`href`) permanece clicável durante refresh — só mutações/dados usam `loading`.
        const actionDisabled = Boolean(action.disabled || (loading && !action.href));
        const className = cn(
            styles.railItem,
            action.danger && styles.railItemDanger,
            action.primary && styles.railItemPrimary,
        );
        const content = (
            <>
                {action.icon ? (
                    <span className={styles.railItemIcon} aria-hidden>
                        {action.icon}
                    </span>
                ) : null}
                <span className={styles.railItemLabel}>{action.label}</span>
            </>
        );
        const control =
            action.href && !actionDisabled ? (
                <Link
                    href={action.href}
                    className={className}
                    data-testid={action.testId}
                    onClick={() => action.onClick()}
                    prefetch={false}
                >
                    {content}
                </Link>
            ) : (
                <button
                    type="button"
                    className={className}
                    onClick={() => action.onClick()}
                    disabled={actionDisabled}
                    data-testid={action.testId}
                    aria-busy={loading || undefined}
                >
                    {content}
                </button>
            );
        if (actionDisabled && action.disabledReason) {
            return (
                <Tooltip key={action.key} title={action.disabledReason}>
                    <span className={styles.railDisabledWrap}>{control}</span>
                </Tooltip>
            );
        }
        return <React.Fragment key={action.key}>{control}</React.Fragment>;
    };

    const hasActions = Boolean(
        (resolvedGroups?.length ?? 0) > 0 || (resolvedActions?.length ?? 0) > 0,
    );

    const panel = (
        <aside
            ref={panelRef}
            className={cn(styles.sideBox, isLeft && styles.sideBoxLeft, 'no-print')}
            style={{ ['--detail-opcoes-side-width' as string]: `${width}px` }}
            role="complementary"
            aria-label={ariaLabel}
            data-testid={dataTestId}
            tabIndex={-1}
        >
            <div className={styles.sideBoxBody}>
                {children ? (
                    <section
                        className={styles.railChildrenSection}
                        aria-label={childrenTitle || undefined}
                    >
                        {childrenTitle ? (
                            <span className={styles.railChildrenTitle}>{childrenTitle}</span>
                        ) : null}
                        <div className={styles.railChildren}>{children}</div>
                    </section>
                ) : null}
                {hasActions ? (
                    <div className={styles.railList}>
                        {resolvedGroups?.length
                            ? resolvedGroups.map((group) => (
                                  <div key={group.key} className={styles.railGroup}>
                                      {group.title ? (
                                          <span className={styles.railGroupTitle}>{group.title}</span>
                                      ) : null}
                                      {group.actions.map((action) => renderAction(action))}
                                  </div>
                              ))
                            : (resolvedActions ?? []).map((action) => renderAction(action))}
                    </div>
                ) : null}
            </div>
        </aside>
    );

    const triggerLabel = ariaLabel ? `Abrir ${ariaLabel}` : 'Abrir opções';

    return createPortal(
        pinned ? (
            panel
        ) : (
            <>
                <button
                    type="button"
                    className={cn(
                        styles.floatTrigger,
                        isLeft ? styles.floatTriggerLeft : styles.floatTriggerRight,
                        'no-print',
                    )}
                    aria-expanded={overlayOpen}
                    aria-label={overlayOpen ? 'Fechar opções' : triggerLabel}
                    data-testid="detail-opcoes-float-trigger"
                    onClick={() => setOverlayOpen((open) => !open)}
                >
                    {isLeft ? (
                        overlayOpen ? (
                            <ChevronLeft size={18} aria-hidden />
                        ) : (
                            <ChevronRight size={18} aria-hidden />
                        )
                    ) : overlayOpen ? (
                        <ChevronRight size={18} aria-hidden />
                    ) : (
                        <ChevronLeft size={18} aria-hidden />
                    )}
                </button>
                {overlayOpen ? (
                    <>
                        <button
                            type="button"
                            className={cn(styles.overlayMask, 'no-print')}
                            aria-label="Fechar opções"
                            onClick={() => setOverlayOpen(false)}
                        />
                        {panel}
                    </>
                ) : null}
            </>
        ),
        document.body,
    );
}
