'use client';

import React, { useState, useEffect, useRef, useCallback } from 'react';
import { createPortal } from 'react-dom';
import {
    Input,
    AutoComplete,
    Empty,
    Typography,
    Space,
    Tag,
    Button,
} from 'antd';
import type { InputRef } from 'antd';
import {
    Bug,
    CheckSquare,
    Crosshair,
    File,
    FolderKanban,
    Headset,
    Layers,
    Search,
    Users,
    X,
} from 'lucide-react';
import { ICON_SIZE_MD } from '@/components/icons';
import { useRouter, usePathname } from 'next/navigation';
import apiClient from '@/lib/api/client';
import { API_ENDPOINTS } from '@/lib/api/endpoints';
import { useMediaQuery } from '@/hooks/useMediaQuery';
import { suporteChamadoDetalhePath } from '@/features/suporte/chamados/chamadosPaths';
import { MEDIA_LAYOUT_SIDEBAR_DRAWER } from '@/constants/breakpoints';
import {
    GLOBAL_SEARCH_OPEN_EVENT,
} from '@/lib/search/globalSearchEvents';
import styles from './GlobalSearch.module.scss';

const { Text } = Typography;

interface SearchResult {
    type: 'projeto' | 'negocio' | 'cliente' | 'bug' | 'tarefa' | 'chamado' | 'epico';
    id: number;
    title: string;
    subtitle?: string;
    url: string;
}

const ACAO_VER_TODOS = 'Ver todos os resultados no projeto';
const ACAO_AMPLIAR = 'Buscar em todo o sistema';

export interface GlobalSearchProps {
    /**
     * Restringe a busca a um projeto, em vez de varrer o sistema.
     *
     * Usado pela shell de projeto: quem está dentro de um projeto quase sempre
     * procura conteúdo dele, e a listagem completa continua a uma opção de
     * distância (a própria busca oferece a saída para o sistema todo).
     */
    escopoProjeto?: { id: string; nome?: string };
}

function getIcon(type: string) {
    switch (type) {
        case 'projeto':
            return <FolderKanban size={ICON_SIZE_MD} style={{ color: '#27132e' }} aria-hidden />;
        case 'negocio':
            return <Crosshair size={ICON_SIZE_MD} style={{ color: '#9b59b6' }} aria-hidden />;
        case 'bug':
            return <Bug size={ICON_SIZE_MD} style={{ color: '#e3402d' }} aria-hidden />;
        case 'tarefa':
            return <CheckSquare size={ICON_SIZE_MD} style={{ color: '#2563eb' }} aria-hidden />;
        case 'chamado':
            return <Headset size={ICON_SIZE_MD} style={{ color: '#d97706' }} aria-hidden />;
        case 'cliente':
            return <Users size={ICON_SIZE_MD} style={{ color: '#059669' }} aria-hidden />;
        case 'epico':
            return <Layers size={ICON_SIZE_MD} style={{ color: '#7c3aed' }} aria-hidden />;
        default:
            return <File size={ICON_SIZE_MD} aria-hidden />;
    }
}

function getTagColor(type: string) {
    switch (type) {
        case 'projeto':
            return 'purple';
        case 'negocio':
            return 'purple';
        case 'bug':
            return 'red';
        case 'tarefa':
            return 'blue';
        case 'chamado':
            return 'orange';
        case 'cliente':
            return 'green';
        case 'epico':
            return 'geekblue';
        default:
            return 'default';
    }
}

function getTypeLabel(type: string) {
    switch (type) {
        case 'projeto':
            return 'Projeto';
        case 'negocio':
            return 'Negócio';
        case 'bug':
            return 'Bug';
        case 'tarefa':
            return 'Tarefa';
        case 'chamado':
            return 'Chamado';
        case 'cliente':
            return 'Cliente';
        case 'epico':
            return 'Épico';
        default:
            return type;
    }
}

/**
 * Busca restrita a um projeto: épicos e tarefas do próprio projeto.
 *
 * Usa as mesmas fontes da página de resultados (`/projetos/[id]/search`), para que
 * o que aparece aqui e o que aparece lá não se contradigam. O endpoint de épicos é
 * por projeto e não recebe termo, então filtra-se em memória; nas tarefas o termo
 * vai ao servidor e o projeto é reconfirmado no cliente, porque a listagem pode
 * devolver itens sem projeto associado.
 */
async function buscarNoProjeto(
    query: string,
    projetoId: string,
    signal: AbortSignal,
): Promise<SearchResult[]> {
    const termo = query.trim().toLowerCase();
    const [epicosRes, tarefasRes] = await Promise.allSettled([
        apiClient.get(API_ENDPOINTS.epicos.index(projetoId), { signal }),
        apiClient.get(API_ENDPOINTS.desenvolvimento.tarefas.index, {
            params: { search: query, projeto_id: projetoId, per_page: 8 },
            signal,
        }),
    ]);

    const results: SearchResult[] = [];
    const combina = (texto?: string | null) =>
        Boolean(texto && termo && String(texto).toLowerCase().includes(termo));

    if (tarefasRes.status === 'fulfilled' && tarefasRes.value.data?.data) {
        (
            tarefasRes.value.data.data as Array<{
                id: number;
                titulo?: string;
                nome?: string;
                descricao?: string;
                chave_tarefa?: string;
                chave?: string;
                projeto_id?: number | null;
            }>
        )
            .filter(
                (t) => t.projeto_id == null || t.projeto_id === Number(projetoId),
            )
            .forEach((t) => {
                results.push({
                    type: 'tarefa',
                    id: t.id,
                    title: t.titulo || t.nome || `Tarefa ${t.id}`,
                    subtitle: t.chave_tarefa || t.chave || t.descricao,
                    url: `/minhas-tarefas/tarefas/${t.id}`,
                });
            });
    }

    if (epicosRes.status === 'fulfilled' && epicosRes.value.data?.data) {
        (
            epicosRes.value.data.data as Array<{
                id: number;
                nome?: string;
                codigo?: string;
                descricao?: string;
            }>
        )
            .filter((e) => combina(e.nome) || combina(e.codigo) || combina(e.descricao))
            .slice(0, 8)
            .forEach((e) => {
                results.push({
                    type: 'epico',
                    id: e.id,
                    title: e.nome || e.codigo || `Épico ${e.id}`,
                    subtitle: e.codigo || e.descricao,
                    url: `/projetos/${projetoId}/epicos/${e.id}`,
                });
            });
    }

    return results;
}

/** Opção do autocomplete para um resultado de busca. */
function montarOpcao(result: SearchResult, onNavigate: (url: string) => void) {
    return {
        value: result.title,
        label: (
            <div
                onClick={() => onNavigate(result.url)}
                style={{ cursor: 'pointer', padding: '4px 0' }}
            >
                <Space>
                    {getIcon(result.type)}
                    <div style={{ flex: 1 }}>
                        <div style={{ fontWeight: 500 }}>{result.title}</div>
                        {result.subtitle && (
                            <Text type="secondary" style={{ fontSize: 12 }}>
                                {result.subtitle.length > 50
                                    ? `${result.subtitle.substring(0, 50)}...`
                                    : result.subtitle}
                            </Text>
                        )}
                    </div>
                    <Tag color={getTagColor(result.type)}>{getTypeLabel(result.type)}</Tag>
                </Space>
            </div>
        ),
        url: result.url,
    };
}

export default function GlobalSearch({ escopoProjeto }: GlobalSearchProps = {}) {
    const router = useRouter();
    const pathname = usePathname();
    const isDrawerViewport = useMediaQuery(MEDIA_LAYOUT_SIDEBAR_DRAWER);
    const [mobileSearchOpen, setMobileSearchOpen] = useState(false);
    const inputRef = useRef<InputRef>(null);

    const [searchValue, setSearchValue] = useState('');
    const [options, setOptions] = useState<
        Array<{ value: string; label: React.ReactNode; url: string }>
    >([]);
    const [loading, setLoading] = useState(false);
    const timeoutRef = useRef<ReturnType<typeof setTimeout>>();
    const searchAbortRef = useRef<AbortController | null>(null);
    /*
     * Saída do escopo: dentro de um projeto a busca começa restrita, e esta bandeira
     * permite ampliar para o sistema sem trocar de campo. Volta ao escopo do projeto
     * quando a busca é limpa ou a rota muda, para o padrão não ficar «pegado».
     */
    const [ampliarParaSistema, setAmpliarParaSistema] = useState(false);
    /*
     * Só é usado ao ampliar o escopo: escolher uma opção fecha a lista, e sem reabrir
     * o resultado da nova busca ficaria escondido. Volta a solto no primeiro fecho.
     */
    const [reabrirLista, setReabrirLista] = useState(false);
    /* Escolher a opção dispara um fecho imediato, que é justamente o que se ignora. */
    const ignorarProximoFechoRef = useRef(false);
    /*
     * Só o id entra nas dependências: `escopoProjeto` é objeto e, se o pai o criar
     * a cada render, a busca seria recriada e o efeito de debounce reiniciaria sem
     * parar.
     */
    const escopoProjetoId = escopoProjeto?.id ?? null;
    const escopoAtivo = escopoProjetoId != null && !ampliarParaSistema;

    const closeMobileSearch = useCallback(() => {
        setMobileSearchOpen(false);
    }, []);

    const openSearch = useCallback(() => {
        if (isDrawerViewport) {
            setMobileSearchOpen(true);
            return;
        }
        inputRef.current?.focus({ preventScroll: true });
    }, [isDrawerViewport]);

    useEffect(() => {
        closeMobileSearch();
        setAmpliarParaSistema(false);
    }, [pathname, closeMobileSearch]);

    useEffect(() => {
        const onOpen = () => openSearch();
        window.addEventListener(GLOBAL_SEARCH_OPEN_EVENT, onOpen);
        return () => window.removeEventListener(GLOBAL_SEARCH_OPEN_EVENT, onOpen);
    }, [openSearch]);

    useEffect(() => {
        if (!mobileSearchOpen || !isDrawerViewport) return;
        const id = window.setTimeout(() => {
            inputRef.current?.focus();
        }, 50);
        return () => clearTimeout(id);
    }, [mobileSearchOpen, isDrawerViewport]);

    useEffect(() => {
        if (!mobileSearchOpen || !isDrawerViewport) return;
        const onKey = (e: KeyboardEvent) => {
            if (e.key === 'Escape') {
                e.preventDefault();
                closeMobileSearch();
            }
        };
        window.addEventListener('keydown', onKey);
        return () => window.removeEventListener('keydown', onKey);
    }, [mobileSearchOpen, isDrawerViewport, closeMobileSearch]);

    useEffect(() => {
        if (!isDrawerViewport || !mobileSearchOpen) return;
        const prev = document.body.style.overflow;
        document.body.style.overflow = 'hidden';
        return () => {
            document.body.style.overflow = prev;
        };
    }, [isDrawerViewport, mobileSearchOpen]);

    const performSearch = useCallback(
        async (query: string) => {
            searchAbortRef.current?.abort();
            const controller = new AbortController();
            searchAbortRef.current = controller;
            const { signal } = controller;
            try {
                setLoading(true);

                if (escopoAtivo && escopoProjetoId) {
                    const doProjeto = await buscarNoProjeto(query, escopoProjetoId, signal);
                    if (signal.aborted) {
                        return;
                    }
                    setOptions([
                        /* Poucos resultados de propósito: com as duas saídas no fim, tudo
                           tem de caber na altura da lista sem obrigar a rolar. */
                        ...doProjeto
                            .slice(0, 5)
                            .map((r: any) => montarOpcao(r, (url) => router.push(url))),
                        {
                            value: ACAO_VER_TODOS,
                            label: <div className={styles.acaoEscopo}>{ACAO_VER_TODOS}</div>,
                            url: `/projetos/${escopoProjetoId}/search?q=${encodeURIComponent(query)}`,
                        },
                        {
                            value: ACAO_AMPLIAR,
                            label: (
                                <div className={styles.acaoEscopoSecundaria}>{ACAO_AMPLIAR}</div>
                            ),
                            url: '',
                        },
                    ]);
                    return;
                }

                const [projetosRes, negociosRes, bugsRes, tarefasRes, chamadosRes, clientesRes] = await Promise.allSettled([
                    apiClient.get(API_ENDPOINTS.projetos.index, {
                        params: { search: query, per_page: 5 },
                        signal,
                    }),
                    apiClient.get(API_ENDPOINTS.crm.negocios.index, {
                        params: { search: query, per_page: 5 },
                        signal,
                    }),
                    apiClient.get(API_ENDPOINTS.qa.bugs.index, {
                        params: { search: query, per_page: 5 },
                        signal,
                    }),
                    apiClient.get(API_ENDPOINTS.desenvolvimento.tarefas.index, {
                        params: { search: query, per_page: 5 },
                        signal,
                    }),
                    apiClient.get(API_ENDPOINTS.suporte.chamados.index, {
                        params: { search: query, per_page: 5 },
                        signal,
                    }),
                    apiClient.get(API_ENDPOINTS.gestao.clientes.index, {
                        params: { search: query, per_page: 5 },
                        signal,
                    }),
                ]);

                if (signal.aborted) {
                    return;
                }

                const results: SearchResult[] = [];

                if (projetosRes.status === 'fulfilled' && projetosRes.value.data?.data) {
                    projetosRes.value.data.data.forEach(
                        (item: { id: number; nome: string; descricao?: string }) => {
                            results.push({
                                type: 'projeto',
                                id: item.id,
                                title: item.nome,
                                subtitle: item.descricao,
                                url: `/projetos/${item.id}`,
                            });
                        }
                    );
                }

                if (negociosRes.status === 'fulfilled' && negociosRes.value.data?.data) {
                    negociosRes.value.data.data.forEach(
                        (item: { id: number; titulo: string; codigo?: string }) => {
                            results.push({
                                type: 'negocio',
                                id: item.id,
                                title: item.titulo || item.codigo || `Negócio ${item.id}`,
                                subtitle: item.codigo,
                                url: `/crm/negocios/${item.id}`,
                            });
                        }
                    );
                }

                if (bugsRes.status === 'fulfilled' && bugsRes.value.data?.data) {
                    bugsRes.value.data.data.forEach(
                        (item: { id: number; titulo: string; descricao?: string }) => {
                            results.push({
                                type: 'bug',
                                id: item.id,
                                title: item.titulo,
                                subtitle: item.descricao,
                                url: `/qa/bugs/${item.id}`,
                            });
                        }
                    );
                }
                if (tarefasRes.status === 'fulfilled' && tarefasRes.value.data?.data) {
                    tarefasRes.value.data.data.forEach(
                        (item: { id: number; titulo?: string; nome?: string; descricao?: string }) => {
                            results.push({
                                type: 'tarefa',
                                id: item.id,
                                title: item.titulo || item.nome || `Tarefa ${item.id}`,
                                subtitle: item.descricao,
                                /* Detalhe da tarefa vive em /minhas-tarefas/tarefas/[tarefaId];
                                   o caminho sem o segmento não corresponde a rota nenhuma. */
                                url: `/minhas-tarefas/tarefas/${item.id}`,
                            });
                        }
                    );
                }

                if (chamadosRes.status === 'fulfilled' && chamadosRes.value.data?.data) {
                    chamadosRes.value.data.data.forEach(
                        (item: { id: number; titulo?: string; codigo?: string; descricao?: string }) => {
                            results.push({
                                type: 'chamado',
                                id: item.id,
                                title: item.titulo || item.codigo || `Chamado ${item.id}`,
                                subtitle: item.descricao || item.codigo,
                                url: suporteChamadoDetalhePath(String(item.id)),
                            });
                        }
                    );
                }

                if (clientesRes.status === 'fulfilled' && clientesRes.value.data?.data) {
                    clientesRes.value.data.data.forEach(
                        (item: { id: number; nome?: string; razao_social?: string; cnpj?: string }) => {
                            results.push({
                                type: 'cliente',
                                id: item.id,
                                title: item.nome || item.razao_social || `Cliente ${item.id}`,
                                subtitle: item.cnpj,
                                url: `/clientes/${item.id}`,
                            });
                        }
                    );
                }

                setOptions(results.map((result) => montarOpcao(result, (url) => router.push(url))));
            } catch (error) {
                if (controller.signal.aborted) {
                    return;
                }
                console.error('Erro na busca:', error);
            } finally {
                if (!controller.signal.aborted) {
                    setLoading(false);
                }
            }
        },
        [router, escopoAtivo, escopoProjetoId]
    );

    useEffect(() => {
        if (timeoutRef.current) {
            clearTimeout(timeoutRef.current);
        }

        if (searchValue.length < 2) {
            setOptions([]);
            setLoading(false);
            setAmpliarParaSistema(false);
            return;
        }

        timeoutRef.current = setTimeout(() => {
            performSearch(searchValue);
        }, 300);

        return () => {
            if (timeoutRef.current) {
                clearTimeout(timeoutRef.current);
            }
            searchAbortRef.current?.abort();
        };
    }, [searchValue, performSearch]);

    const handleSelect = (value: string, option: { url?: string }) => {
        if (value === ACAO_AMPLIAR) {
            /*
             * Troca o escopo mantendo o termo. O autocomplete fecha ao escolher uma
             * opção, então devolve-se o foco para a lista reabrir já com o resultado
             * da busca ampliada — sem isso a ação parecia não fazer nada.
             */
            setAmpliarParaSistema(true);
            setReabrirLista(true);
            ignorarProximoFechoRef.current = true;
            window.setTimeout(() => inputRef.current?.focus(), 0);
            return;
        }
        if (option.url) {
            router.push(option.url);
            setSearchValue('');
            setOptions([]);
            closeMobileSearch();
        }
    };

    /* O campo diz onde está olhando: dentro do projeto o alcance é outro. */
    const placeholderBusca = escopoAtivo
        ? `Buscar em ${escopoProjeto?.nome?.trim() || 'este projeto'}…`
        : 'Buscar projetos, negócios, bugs, tarefas, chamados e clientes...';

    const autocomplete = (
        <AutoComplete
            value={searchValue}
            /*
             * Escolher uma das duas saídas não deve reescrever o termo: o autocomplete
             * propaga o `value` da opção pelo onChange, e nelas esse valor é o rótulo.
             */
            onChange={(valor) => {
                if (valor !== ACAO_VER_TODOS && valor !== ACAO_AMPLIAR) {
                    setSearchValue(valor);
                }
            }}
            onSelect={handleSelect}
            open={reabrirLista ? true : undefined}
            onOpenChange={(aberto) => {
                if (aberto) {
                    return;
                }
                if (ignorarProximoFechoRef.current) {
                    ignorarProximoFechoRef.current = false;
                    return;
                }
                setReabrirLista(false);
            }}
            /* Lista mais alta no escopo do projeto: as saídas ficam no fim e precisam
               de aparecer sem rolagem. */
            listHeight={escopoAtivo ? 480 : undefined}
            options={options}
            className="global-search-autocomplete"
            style={{ width: '100%', minWidth: 0, maxWidth: isDrawerViewport ? undefined : 300 }}
            notFoundContent={
                loading ? (
                    'Buscando...'
                ) : (
                    <Empty
                        description={
                            escopoAtivo
                                ? 'Nada encontrado neste projeto'
                                : 'Nenhum resultado encontrado'
                        }
                    />
                )
            }
            allowClear
            styles={{
                popup: {
                    root: {
                        ...(isDrawerViewport
                            ? { zIndex: 1070 }
                            : /* Piso de largura: o campo pode ser estreito (header do
                                 projeto) e a lista herda a largura do campo, cortando
                                 título, subtítulo e etiqueta de tipo. */
                              { minWidth: 340 }),
                    },
                },
            }}
        >
            <Input
                ref={inputRef}
                prefix={<Search size={ICON_SIZE_MD} aria-hidden />}
                placeholder={placeholderBusca}
                aria-label={placeholderBusca}
                size="large"
                data-testid="global-search-input"
            />
        </AutoComplete>
    );

    if (isDrawerViewport) {
        return (
            <>
                <Button
                    type="text"
                    icon={<Search size={ICON_SIZE_MD} aria-hidden />}
                    onClick={openSearch}
                    className={styles.mobileSearchTrigger}
                    aria-label={escopoAtivo ? 'Abrir busca no projeto' : 'Abrir busca'}
                    title="Buscar (Ctrl+K)"
                    data-testid="global-search-trigger"
                />
                {mobileSearchOpen &&
                    typeof document !== 'undefined' &&
                    createPortal(
                        <>
                            <div
                                className={styles.backdrop}
                                role="presentation"
                                aria-hidden
                                onClick={closeMobileSearch}
                            />
                            <div
                                className={styles.sheet}
                                role="dialog"
                                aria-modal="true"
                                aria-label={escopoAtivo ? 'Busca no projeto' : 'Busca global'}
                            >
                                <Button
                                    type="text"
                                    icon={<X size={ICON_SIZE_MD} aria-hidden />}
                                    onClick={closeMobileSearch}
                                    className={styles.sheetCloseBtn}
                                    aria-label="Fechar busca"
                                    title="Fechar"
                                />
                                <div className={styles.sheetInput}>{autocomplete}</div>
                            </div>
                        </>,
                        document.body
                    )}
            </>
        );
    }

    return <div className={styles.desktopSearch}>{autocomplete}</div>;
}
