/**
 * Молния: разряд + вспышка с тенями. Разряд — излучающий слой частиц
 * (lit: false): зигзаг-канал от земли вверх с парой ветвей; форма
 * детерминирована номером удара (кадр-равенства гейтов). Вспышка —
 * отдельный направленный свет рендера (VoxelRenderer.setLightning): свет
 * бьёт из разряда вниз с лёгким наклоном — тени от кастеров качаются
 * (та же shadow map-механика, что у солнца); ночью вспышка читается ярче —
 * днём она тонет в солнечном свете. Lit-частицы (дождь/снег) и туман
 * подсвечиваются вспышкой через env-свет (lightningEnvLight — вклад в
 * uEnv/uFogColor в setSun). Огибающая мерцания и геометрия разряда
 * — чистые функции (тесты без WebGL). Пока удар по кнопке дев-панели;
 * позже из этого собирается гроза (серия ударов) при необходимости в игре.
 */
import type { ParticleLayer } from './particles';
import type { VoxelRenderer } from './voxelRenderer';

/** Высота разряда над точкой удара (юниты): полукадр по вертикали мира
 *  ~viewSize/cos(elev) ≈ 176 — разряд уходит за верхний край кадра. */
export const LIGHTNING_TOP = 176;

/** Длительность вспышки с мерцанием (с). */
export const LIGHTNING_DURATION = 0.55;

/** Сила вспышки-света при пике огибающей (множитель к направленному свету). */
export const LIGHTNING_FLASH = 5;

/** Цвет канала разряда (холодный бело-голубой). */
const BOLT_COLOR = '#eef3ff';

/** Цвет света вспышки (совпадает со светом рендера, 0xdfe8ff). */
export const LIGHTNING_COLOR: readonly [number, number, number] = [0.874, 0.91, 1.0];

/** Доля вспышки в env-свете частиц/тумана: билборд ловит свет со всех
 *  сторон, а вспышка направленная — берём часть её силы. */
export const LIGHTNING_ENV_SHARE = 0.2;

/** Вклад вспышки в env-свет частиц и тумана: дождь/снег (lit-слои) и
 *  туман подсвечиваются разрядом вместе с миром. Чистая функция. */
export function lightningEnvLight(k: number, nightK: number): [number, number, number] {
    const f = Math.max(0, k) * LIGHTNING_FLASH * (1 + nightK) * LIGHTNING_ENV_SHARE;
    return [LIGHTNING_COLOR[0] * f, LIGHTNING_COLOR[1] * f, LIGHTNING_COLOR[2] * f];
}

/** Псевдослучай 0..1 от пары чисел (sin-хэш): детерминирован, без состояния. */
function boltHash(a: number, b: number): number {
    const s = Math.sin(a * 127.1 + b * 311.7) * 43758.5453;
    return s - Math.floor(s);
}

/** Огибающая мерцания вспышки 0..1: резкий пик в момент удара и два
 *  рестрайка (настоящие молнии бьют 2–3 раза), затухание к нулю.
 *  Чистая функция — тесты без WebGL. */
export function lightningEnvelope(dt: number): number {
    if (dt < 0 || dt >= LIGHTNING_DURATION) return 0;
    const main = Math.exp(-dt * 9); // первый (главный) разряд
    const re1 = 0.7 * Math.exp(-Math.pow((dt - 0.18) * 14, 2)); // рестрайк
    const re2 = 0.5 * Math.exp(-Math.pow((dt - 0.34) * 16, 2)); // второй
    return Math.min(1, main + re1 + re2);
}

/** Точка геометрии разряда (для слоя частиц): позиция, размер, доля альфы. */
export interface BoltPoint {
    pos: [number, number, number];
    size: number;
    alpha: number;
}

/** Геометрия разряда: непрерывный зигзаг-канал от земли вверх (узлы каждые
 *  3 юнита + ореол вокруг каждого) и две ветви вбок-вниз. Зигзаг — сумма
 *  двух синусов (ограничен, канал не уходит от точки удара), фазы/частоты
 *  от seed (номер удара) — чистая функция, детерминированная. */
export function boltPoints(seed: number, at: readonly [number, number, number]): BoltPoint[] {
    const pts: BoltPoint[] = [];
    const steps = Math.round(LIGHTNING_TOP / 3); // 58 узлов канала
    const p1 = boltHash(seed, 1.3) * Math.PI * 2; // фазы и частоты зигзага
    const p2 = boltHash(seed, 2.9) * Math.PI * 2;
    const f1 = 0.55 + boltHash(seed, 4.1) * 0.3;
    const f2 = 0.19 + boltHash(seed, 5.7) * 0.1;
    const b1 = 6 + Math.floor(boltHash(seed, 6.1) * 14); // узлы ветвления
    const b2 = 28 + Math.floor(boltHash(seed, 7.3) * 20);
    for (let i = 0; i < steps; i++) {
        const y = at[1] + i * 3;
        const x = at[0] + Math.sin(i * f1 + p1) * 2.6 + Math.sin(i * f2 + p2) * 1.9;
        const z = at[2] + Math.cos(i * f1 * 0.9 + p2) * 2.6 + Math.sin(i * f2 * 1.1 + p1) * 1.9;
        pts.push({ pos: [x, y, z], size: 1.35 - i * 0.008, alpha: 1 });
        pts.push({ pos: [x, y, z], size: 3.6, alpha: 0.22 }); // мягкий ореол
        if (i === b1 || i === b2) {
            // ветвь: 5 сегментов вбок и вниз от узла канала
            const ang = boltHash(seed, 10 + i) * Math.PI * 2;
            const dx = Math.cos(ang);
            const dz = Math.sin(ang);
            let bx = x;
            let by = y - 1;
            let bz = z;
            for (let j = 0; j < 5; j++) {
                bx += dx * 2.4;
                by -= 2.2;
                bz += dz * 2.4;
                pts.push({ pos: [bx, by, bz], size: 0.6, alpha: 0.7 });
            }
        }
    }
    return pts;
}

/** Снапшот молнии (мост/гейты): активна ли вспышка, счётчик ударов,
 *  текущая огибающая и точка удара. */
export interface LightningInfo {
    active: boolean;
    strikes: number;
    flash: number;
    at: [number, number, number] | null;
}

/** Молния: слой-разряд + свет вспышки. `strike()` бьёт в заданную точку
 *  земли [x, y, z]; без аргумента — в случайную точку окна видимости
 *  (детерминированно от номера удара, y — фокус камеры). `update(t)` гонит
 *  огибающую: точки разряда и setLightning рендера. */
export class LightningSystem {
    private readonly renderer: VoxelRenderer;
    private readonly layer: ParticleLayer;
    private strikeT = -1; // время начала вспышки (-1 — нет)
    private seed = 0;
    private at: [number, number, number] | null = null;
    private strikes = 0;
    private flashK = 0;
    private lastT = 0;

    constructor(renderer: VoxelRenderer) {
        this.renderer = renderer;
        // разряд излучает сам — не освещаемый слой (вспышка и есть свет)
        this.layer = renderer.addParticles({ lit: false });
    }

    /** Удар молнии: at — точка земли [x, y, z]; без аргумента — случайная
     *  точка окна видимости (детерминированно от номера удара). */
    strike(at?: readonly [number, number, number]): void {
        this.strikes++;
        this.seed = this.strikes * 7919 + 17;
        if (at) {
            this.at = [at[0], at[1], at[2]];
        } else {
            const w = this.renderer.visibleWindow(0, LIGHTNING_TOP);
            const f = this.renderer.focusPoint();
            const rx = boltHash(this.seed, 3.1);
            const rz = boltHash(this.seed, 7.7);
            this.at = [w.minX + (w.maxX - w.minX) * rx, f[1], w.minZ + (w.maxZ - w.minZ) * rz];
        }
        this.strikeT = this.lastT;
        this.update(this.lastT);
    }

    /** Тик: огибающая мерцания → точки разряда + свет вспышки; по истечении
     *  длительности — погасить слой и свет. */
    update(t: number): void {
        this.lastT = t;
        if (this.strikeT < 0 || !this.at) return;
        const dt = t - this.strikeT;
        const e = lightningEnvelope(dt);
        if (e <= 0) {
            this.layer.update([]);
            this.renderer.setLightning(null, 0);
            this.strikeT = -1;
            this.flashK = 0;
            return;
        }
        this.flashK = e;
        this.layer.update(boltPoints(this.seed, this.at).map((p) => ({
            pos: p.pos, color: BOLT_COLOR, alpha: p.alpha * e, size: p.size,
        })));
        this.renderer.setLightning(this.at, e);
    }

    /** Снапшот для моста/гейтов. */
    info(): LightningInfo {
        return {
            active: this.strikeT >= 0,
            strikes: this.strikes,
            flash: this.flashK,
            at: this.at ? [...this.at] : null,
        };
    }

    dispose(): void {
        this.layer.dispose();
        this.strikeT = -1;
    }
}