/**
 * Environment — управление окружением сцены: время суток, скайбокс, освещение,
 * туман. Работает поверх существующих lights/clearColor BabylonScene.
 *
 * Время суток (timeOfDay): 0..24 часов.
 *   06:00 — рассвет (золотистый свет, оранжево-розовый горизонт)
 *   12:00 — день (яркое солнце, голубое небо)
 *   18:00 — закат (красно-оранжевое небо, длинные тени)
 *   00:00 — ночь (тёмно-синее небо, луна, приглушённый свет)
 *
 * Пресеты:
 *   'day'    — фиксированный день (12:00)
 *   'sunset' — закат (18:30)
 *   'night'  — ночь (00:00)
 *   'dawn'   — рассвет (06:00)
 *   'cycle'  — авто-цикл (timeSpeed контролирует скорость)
 *
 * Public API:
 *   setPreset(preset)
 *   setTimeOfDay(hour)              — 0..24
 *   setTimeSpeed(seconds_per_hour)  — для cycle-режима
 *   tick(dt)                         — вызывать в render-loop
 *   serialize() / load(data)
 */
import {
    Color3, Color4, Vector3,
    MeshBuilder, StandardMaterial,
} from '@babylonjs/core';

/**
 * Цвета по часам — интерполируем между ключевыми моментами.
 * Каждая запись: [час, sky, fog, sunDir, sunIntensity, sunColor, ambientGround, hemiIntensity]
 */
const KEYFRAMES = [
    // hour, sky color (RGB 0..1), sun direction, sunIntensity, sunColor, hemiGround, hemiIntensity
    { hour: 0,  sky: [0.05, 0.05, 0.15], sunDir: [-0.3, -1, 0.2], sunInt: 0.05, sunCol: [0.4, 0.5, 0.7], hemiGround: [0.05, 0.05, 0.1], hemiInt: 0.15 },
    { hour: 5,  sky: [0.15, 0.12, 0.25], sunDir: [-0.6, -0.5, 0.3], sunInt: 0.15, sunCol: [0.6, 0.4, 0.4], hemiGround: [0.1, 0.08, 0.15], hemiInt: 0.25 },
    { hour: 7,  sky: [0.95, 0.7, 0.5],   sunDir: [-0.8, -0.4, 0.2], sunInt: 0.6,  sunCol: [1.0, 0.8, 0.5], hemiGround: [0.3, 0.25, 0.3], hemiInt: 0.5 },
    { hour: 12, sky: [0.5, 0.7, 0.95],   sunDir: [-0.3, -1, -0.2],  sunInt: 0.9,  sunCol: [1.0, 1.0, 0.95], hemiGround: [0.3, 0.3, 0.4], hemiInt: 0.65 },
    { hour: 17, sky: [0.6, 0.75, 0.9],   sunDir: [0.4, -0.6, -0.3], sunInt: 0.7,  sunCol: [1.0, 0.9, 0.7], hemiGround: [0.3, 0.3, 0.35], hemiInt: 0.55 },
    { hour: 19, sky: [0.95, 0.55, 0.4],  sunDir: [0.7, -0.3, -0.2], sunInt: 0.5,  sunCol: [1.0, 0.6, 0.4], hemiGround: [0.35, 0.25, 0.25], hemiInt: 0.4 },
    { hour: 21, sky: [0.2, 0.18, 0.35],  sunDir: [0.5, -0.6, 0.2],  sunInt: 0.15, sunCol: [0.5, 0.5, 0.7], hemiGround: [0.1, 0.1, 0.2], hemiInt: 0.2 },
    { hour: 24, sky: [0.05, 0.05, 0.15], sunDir: [-0.3, -1, 0.2], sunInt: 0.05, sunCol: [0.4, 0.5, 0.7], hemiGround: [0.05, 0.05, 0.1], hemiInt: 0.15 },
];

const PRESETS = {
    day:    { hour: 12, autoCycle: false },
    sunset: { hour: 18.5, autoCycle: false },
    night:  { hour: 0, autoCycle: false },
    dawn:   { hour: 6, autoCycle: false },
    cycle:  { hour: 12, autoCycle: true },
};

function lerp(a, b, t) { return a + (b - a) * t; }
function lerpVec3(a, b, t) {
    return [lerp(a[0], b[0], t), lerp(a[1], b[1], t), lerp(a[2], b[2], t)];
}

/** Найти кадры до и после текущего часа и интерполировать. */
function sampleAt(hour) {
    let prev = KEYFRAMES[0], next = KEYFRAMES[KEYFRAMES.length - 1];
    for (let i = 0; i < KEYFRAMES.length - 1; i++) {
        if (KEYFRAMES[i].hour <= hour && KEYFRAMES[i + 1].hour >= hour) {
            prev = KEYFRAMES[i]; next = KEYFRAMES[i + 1]; break;
        }
    }
    const span = next.hour - prev.hour;
    const t = span > 0 ? (hour - prev.hour) / span : 0;
    return {
        sky: lerpVec3(prev.sky, next.sky, t),
        sunDir: lerpVec3(prev.sunDir, next.sunDir, t),
        sunInt: lerp(prev.sunInt, next.sunInt, t),
        sunCol: lerpVec3(prev.sunCol, next.sunCol, t),
        hemiGround: lerpVec3(prev.hemiGround, next.hemiGround, t),
        hemiInt: lerp(prev.hemiInt, next.hemiInt, t),
    };
}

export class Environment {
    constructor(scene, hemiLight, sunLight) {
        this.scene = scene;
        this.hemiLight = hemiLight;
        this.sunLight = sunLight;
        this.preset = 'day';
        this.timeOfDay = 12;        // часов 0..24
        // Длительность фаз цикла day/night в МИНУТАХ реального времени.
        // День — с 06 до 18 (12 игровых часов).
        // Ночь — с 18 до 06 (тоже 12 игровых часов).
        this.dayDurationMin = 5;
        this.nightDurationMin = 3;
        this.fogEnabled = false;
        this.fogColor = [0.7, 0.8, 0.9];
        this.fogDensity = 0.01;
        // Видимые тела на небе (солнце и луна).
        // ВАЖНО (задача 16): единое небо рисует SkyboxManager. Environment больше
        // НЕ рисует свою жёлтую сферу/луну — иначе на небе два солнца. Здесь
        // остаётся только управление светом (направление/яркость/ambient).
        this._drawSkyBodies = false;
        this._sunMesh = null;
        this._moonMesh = null;
        if (this._drawSkyBodies) this._createSkyBodies();
        this._applyTime();
    }

    _createSkyBodies() {
        // Солнце — жёлтая светящаяся сфера
        const sun = MeshBuilder.CreateSphere('skySun', { diameter: 8, segments: 12 }, this.scene);
        const sunMat = new StandardMaterial('skySunMat', this.scene);
        sunMat.diffuseColor = new Color3(0, 0, 0);
        sunMat.emissiveColor = new Color3(1, 0.95, 0.7);
        sunMat.specularColor = new Color3(0, 0, 0);
        sunMat.disableLighting = true;
        sun.material = sunMat;
        sun.isPickable = false;
        sun.renderingGroupId = 0;
        this._sunMesh = sun;

        // Луна — серовато-белая сфера
        const moon = MeshBuilder.CreateSphere('skyMoon', { diameter: 6, segments: 12 }, this.scene);
        const moonMat = new StandardMaterial('skyMoonMat', this.scene);
        moonMat.diffuseColor = new Color3(0, 0, 0);
        moonMat.emissiveColor = new Color3(0.85, 0.85, 0.95);
        moonMat.specularColor = new Color3(0, 0, 0);
        moonMat.disableLighting = true;
        moon.material = moonMat;
        moon.isPickable = false;
        moon.renderingGroupId = 0;
        this._moonMesh = moon;
    }

    /** Вычислить позицию небесного тела по фазе 0..1 (0=восход, 0.5=зенит, 1=закат). */
    _skyBodyPosition(phase) {
        // Дуга по полусфере радиусом R, центр в (0, 0, 0).
        // phase=0 → восток (восход на горизонте);
        // phase=0.5 → зенит;
        // phase=1 → запад (заход).
        const R = 80;
        const angle = phase * Math.PI;     // 0..π
        return new Vector3(
            Math.cos(angle) * R,           // восход +X → закат -X
            Math.sin(angle) * R,           // 0 → R → 0
            0
        );
    }

    setPreset(preset) {
        if (!PRESETS[preset]) return;
        this.preset = preset;
        const cfg = PRESETS[preset];
        this.timeOfDay = cfg.hour;
        this._applyTime();
    }

    setTimeOfDay(hour) {
        this.timeOfDay = ((hour % 24) + 24) % 24;
        this._applyTime();
    }

    /** Установить длительность фазы дня и ночи в минутах реального времени. */
    setCycleDuration(dayMin, nightMin) {
        if (typeof dayMin === 'number' && dayMin > 0) this.dayDurationMin = dayMin;
        if (typeof nightMin === 'number' && nightMin > 0) this.nightDurationMin = nightMin;
    }

    /** Совместимость со старым setTimeSpeed (sec/hour). Игнорируется в новом API. */
    setTimeSpeed() { /* deprecated */ }

    setFog(enabled, color, density) {
        this.fogEnabled = !!enabled;
        if (Array.isArray(color)) this.fogColor = color;
        if (density != null) this.fogDensity = density;
        this._applyFog();
    }

    /** В render-loop вызвать каждый кадр (для авто-цикла). */
    tick(dt) {
        if (this.preset !== 'cycle') return;
        // День: 06 → 18 (12 игровых часов) идёт за dayDurationMin минут реального.
        // Ночь: 18 → 06 (12 игровых часов) идёт за nightDurationMin минут реального.
        const isDayTime = this.timeOfDay >= 6 && this.timeOfDay < 18;
        const phaseSeconds = (isDayTime ? this.dayDurationMin : this.nightDurationMin) * 60;
        // За phaseSeconds игровое время продвигается на 12 часов.
        const hoursPerSecond = 12 / phaseSeconds;
        this.timeOfDay = (this.timeOfDay + dt * hoursPerSecond) % 24;
        this._applyTime();
    }

    /** Применить текущее время суток к небу и освещению. */
    _applyTime() {
        const s = sampleAt(this.timeOfDay);
        if (this.scene) {
            this.scene.clearColor = new Color4(s.sky[0], s.sky[1], s.sky[2], 1.0);
        }
        if (this.sunLight) {
            // Babylon DirectionalLight: direction указывает В сторону куда падает свет.
            const dir = new Vector3(s.sunDir[0], s.sunDir[1], s.sunDir[2]);
            dir.normalize();
            this.sunLight.direction = dir;
            this.sunLight.intensity = s.sunInt;
            this.sunLight.diffuse = new Color3(s.sunCol[0], s.sunCol[1], s.sunCol[2]);
        }
        if (this.hemiLight) {
            this.hemiLight.intensity = s.hemiInt;
            this.hemiLight.groundColor = new Color3(s.hemiGround[0], s.hemiGround[1], s.hemiGround[2]);
            this.hemiLight.diffuse = new Color3(s.sky[0], s.sky[1], s.sky[2]);
        }

        // === Позиции солнца и луны ===
        // Солнце видно с 06 до 18 (12 часов). phase = (hour - 6) / 12 → 0..1
        // Луна видна с 18 до 06 (через полночь). phase = ((hour + 6) % 24) / 12 → 0..1
        const h = this.timeOfDay;
        if (this._sunMesh) {
            if (h >= 6 && h <= 18) {
                const phase = (h - 6) / 12;
                this._sunMesh.position = this._skyBodyPosition(phase);
                this._sunMesh.setEnabled(true);
            } else {
                this._sunMesh.setEnabled(false);
            }
        }
        if (this._moonMesh) {
            // Луна видна когда солнце под горизонтом
            if (h <= 6 || h >= 18) {
                // hour 18→0 = phase 0 (восход), hour 0→6 = phase 0.5..1
                const adj = (h + 6) % 24;
                const phase = adj / 12;
                this._moonMesh.position = this._skyBodyPosition(phase);
                this._moonMesh.setEnabled(true);
            } else {
                this._moonMesh.setEnabled(false);
            }
        }

        this._applyFog();
    }

    _applyFog() {
        if (!this.scene) return;
        if (this.fogEnabled) {
            this.scene.fogMode = 2;     // FOGMODE_EXP
            this.scene.fogColor = new Color3(this.fogColor[0], this.fogColor[1], this.fogColor[2]);
            this.scene.fogDensity = this.fogDensity;
        } else {
            this.scene.fogMode = 0;     // FOGMODE_NONE
        }
    }

    serialize() {
        return {
            preset: this.preset,
            timeOfDay: this.timeOfDay,
            dayDurationMin: this.dayDurationMin,
            nightDurationMin: this.nightDurationMin,
            fogEnabled: this.fogEnabled,
            fogColor: this.fogColor,
            fogDensity: this.fogDensity,
        };
    }

    load(data) {
        if (!data) return;
        if (data.preset) this.preset = data.preset;
        if (typeof data.timeOfDay === 'number') this.timeOfDay = data.timeOfDay;
        if (typeof data.dayDurationMin === 'number' && data.dayDurationMin > 0) {
            this.dayDurationMin = data.dayDurationMin;
        }
        if (typeof data.nightDurationMin === 'number' && data.nightDurationMin > 0) {
            this.nightDurationMin = data.nightDurationMin;
        }
        if (typeof data.fogEnabled === 'boolean') this.fogEnabled = data.fogEnabled;
        if (Array.isArray(data.fogColor)) this.fogColor = data.fogColor;
        if (typeof data.fogDensity === 'number') this.fogDensity = data.fogDensity;
        this._applyTime();
    }
}
