import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { listen } from '@tauri-apps/api/event';

import { SegmentedRadio } from './components/SegmentedRadio';
import {
    capLogRows,
    consumeLoadedHistoryRow,
    dedupeBufferedRows,
    formatCount,
    LEVEL_FILTER_MIN,
    LEVEL_FILTER_OPTIONS,
    LEVEL_ORDER,
    lineCountsFromRows,
    type LevelFilter,
    type MainLogHistoryPayload,
    type MainLogLinePayload,
    type MainLogRow,
    rowFromPayload,
    rowsFromHistory,
} from './lib/mainLogs';

import './MainLogsWindow.css';

export const isMainLogsView = (): boolean =>
    new URLSearchParams(window.location.search).get('view') === 'main-logs';

export const MainLogsWindow = () => {
    const [logs, setLogs] = useState<MainLogRow[]>([]);
    const [loading, setLoading] = useState(true);
    const [loadError, setLoadError] = useState<string | null>(null);
    const [regexInput, setRegexInput] = useState('');
    const [levelFilter, setLevelFilter] = useState<LevelFilter>('all');
    const [autoScroll, setAutoScroll] = useState(true);
    const [paused, setPaused] = useState(false);
    const [pausedBufferCount, setPausedBufferCount] = useState(0);
    const [copyStatus, setCopyStatus] = useState<string | null>(null);

    const viewportRef = useRef<HTMLDivElement | null>(null);
    const historyLoadedRef = useRef(false);
    const initialBufferRef = useRef<MainLogRow[]>([]);
    const pausedBufferRef = useRef<MainLogRow[]>([]);
    const pausedRef = useRef(false);
    const lastHistorySequenceRef = useRef(0);
    const historyLineCountsRef = useRef<Map<string, number>>(new Map());

    const appendVisibleRows = useCallback((rows: MainLogRow[]) => {
        setLogs(current => capLogRows([...current, ...rows]));
    }, []);

    const bufferPausedRows = useCallback((rows: MainLogRow[]) => {
        pausedBufferRef.current = capLogRows([...pausedBufferRef.current, ...rows]);
        setPausedBufferCount(pausedBufferRef.current.length);
    }, []);

    useEffect(() => {
        let cancelled = false;
        let unlisten: (() => void) | undefined;

        const handleIncomingRow = (row: MainLogRow) => {
            if (!historyLoadedRef.current) {
                initialBufferRef.current = capLogRows([...initialBufferRef.current, row]);
                return;
            }
            if (
                consumeLoadedHistoryRow(
                    historyLineCountsRef.current,
                    row,
                    lastHistorySequenceRef.current,
                )
            ) {
                return;
            }
            if (pausedRef.current) {
                bufferPausedRows([row]);
                return;
            }
            appendVisibleRows([row]);
        };

        const setup = async () => {
            try {
                unlisten = await listen<MainLogLinePayload>('main-log-line', event => {
                    handleIncomingRow(rowFromPayload(event.payload));
                });

                const history = await invoke<MainLogHistoryPayload>('get_main_logs');
                if (cancelled) return;

                lastHistorySequenceRef.current = history.lastSequence;
                const historyRows = rowsFromHistory(history.contents);
                const historyLineCounts = lineCountsFromRows(historyRows);
                const liveRows = dedupeBufferedRows(
                    historyLineCounts,
                    initialBufferRef.current,
                    lastHistorySequenceRef.current,
                );
                initialBufferRef.current = [];
                historyLineCountsRef.current = historyLineCounts;
                historyLoadedRef.current = true;

                if (pausedRef.current) {
                    setLogs(capLogRows(historyRows));
                    bufferPausedRows(liveRows);
                } else {
                    setLogs(capLogRows([...historyRows, ...liveRows]));
                }
                setLoadError(null);
            } catch (err) {
                if (!cancelled) {
                    historyLoadedRef.current = true;
                    setLoadError(err instanceof Error ? err.message : String(err));
                }
            } finally {
                if (!cancelled) {
                    setLoading(false);
                }
            }
        };

        void setup();

        return () => {
            cancelled = true;
            historyLoadedRef.current = false;
            initialBufferRef.current = [];
            lastHistorySequenceRef.current = 0;
            historyLineCountsRef.current = new Map();
            unlisten?.();
        };
    }, [appendVisibleRows, bufferPausedRows]);

    const { regex, regexError } = useMemo(() => {
        if (!regexInput) {
            return { regex: null as RegExp | null, regexError: null as string | null };
        }
        try {
            return { regex: new RegExp(regexInput, 'i'), regexError: null };
        } catch (e) {
            return { regex: null, regexError: e instanceof Error ? e.message : String(e) };
        }
    }, [regexInput]);

    const filteredRows = useMemo(() => {
        const minLevel = LEVEL_FILTER_MIN[levelFilter];
        return logs.filter(row => {
            if (LEVEL_ORDER[row.level] < minLevel) return false;
            return regex ? regex.test(row.line) : true;
        });
    }, [levelFilter, logs, regex]);

    const lastVisibleRow = filteredRows.length > 0 ? filteredRows[filteredRows.length - 1] : null;

    useEffect(() => {
        if (!autoScroll) return;
        const viewport = viewportRef.current;
        if (!viewport) return;

        requestAnimationFrame(() => {
            viewport.scrollTop = viewport.scrollHeight;
        });
    }, [autoScroll, filteredRows.length, lastVisibleRow?.id]);

    const flushPausedRows = useCallback(() => {
        const buffered = pausedBufferRef.current;
        if (buffered.length === 0) return;
        pausedBufferRef.current = [];
        setPausedBufferCount(0);
        appendVisibleRows(buffered);
    }, [appendVisibleRows]);

    const togglePaused = useCallback(() => {
        if (paused) {
            pausedRef.current = false;
            setPaused(false);
            flushPausedRows();
            return;
        }

        pausedRef.current = true;
        setPaused(true);
    }, [flushPausedRows, paused]);

    const clearLogs = useCallback(() => {
        setLogs([]);
        initialBufferRef.current = [];
        pausedBufferRef.current = [];
        setPausedBufferCount(0);
    }, []);

    const copyFilteredLogs = useCallback(async () => {
        try {
            await navigator.clipboard.writeText(filteredRows.map(row => row.line).join('\n'));
            setCopyStatus('Copied');
        } catch {
            setCopyStatus('Copy failed');
        }
        window.setTimeout(() => setCopyStatus(null), 1600);
    }, [filteredRows]);

    return (
        <main className="main-log-window">
            <div className="main-log-header">
                <h1>Application Logs</h1>
                <div className="main-log-controls">
                    <label className="main-log-toggle">
                        <input
                            type="checkbox"
                            checked={autoScroll}
                            onChange={(e) => setAutoScroll(e.target.checked)}
                        />
                        Auto-scroll
                    </label>
                    <button className="settings-button" onClick={togglePaused}>
                        {paused ? 'Resume' : 'Pause'}
                    </button>
                    <button className="settings-button" onClick={clearLogs} disabled={logs.length === 0}>
                        Clear
                    </button>
                    <button
                        className="settings-button"
                        onClick={() => void copyFilteredLogs()}
                        disabled={filteredRows.length === 0}
                    >
                        Copy
                    </button>
                    {copyStatus && <span className="main-log-copy-status">{copyStatus}</span>}
                </div>
            </div>

            <div className="main-log-filter-row">
                <div className="main-log-level-filter">
                    <SegmentedRadio
                        value={levelFilter}
                        options={LEVEL_FILTER_OPTIONS}
                        onChange={setLevelFilter}
                    />
                </div>
                <input
                    className={`unpack-log-regex main-log-regex ${regexError ? 'invalid' : ''}`}
                    type="text"
                    placeholder="Filter lines by regex (case-insensitive)..."
                    value={regexInput}
                    onChange={(e) => setRegexInput(e.target.value)}
                    title={regexError ?? ''}
                    spellCheck={false}
                />
            </div>

            {regexError && (
                <div className="unpack-log-regex-error">regex error: {regexError}</div>
            )}
            {loadError && (
                <div className="main-log-load-error">load error: {loadError}</div>
            )}

            <div className="main-log-stats">
                {loading ? 'loading' : `showing ${formatCount(filteredRows.length, 'line')} of ${formatCount(logs.length, 'line')}`}
                {paused && pausedBufferCount > 0 && ` - ${formatCount(pausedBufferCount, 'paused line')}`}
            </div>

            <section ref={viewportRef} className="main-log-viewport" aria-live={paused ? 'off' : 'polite'}>
                {filteredRows.length === 0 ? (
                    <div className="main-log-empty">
                        {logs.length === 0 ? 'No application logs recorded yet.' : 'No log lines match the current filters.'}
                    </div>
                ) : filteredRows.map(row => (
                    <div
                        key={row.id}
                        className={`main-log-line level-${row.level.toLowerCase()}`}
                    >
                        {row.line}
                    </div>
                ))}
            </section>
        </main>
    );
};
