import { useEffect, useRef, useState } from 'react';
import { Head } from '@inertiajs/react';
import TataLetakPublik from '@/tata-letak/TataLetakPublik';
import { Ikon } from '@/komponen/Ikon';
import { type KeadaanBerkas } from '@/komponen/KartuUnggah';
import { umumkan } from '@/komponen/Pengumuman';
import { tahapSah } from '@/lib/validasi';
import { pesan, tanya } from '@/lib/dialog';
import { useSimpanOtomatis } from '@/hook/useSimpanOtomatis';
import { useUnggahBerkas, type StatusUnggah } from '@/hook/useUnggahBerkas';
import TahapDataDiri from '@/formulir/tahap/TahapDataDiri';
import TahapAlamat from '@/formulir/tahap/TahapAlamat';
import TahapSekolah from '@/formulir/tahap/TahapSekolah';
import TahapOrangTua from '@/formulir/tahap/TahapOrangTua';
import TahapBerkas from '@/formulir/tahap/TahapBerkas';
import TahapKonfirmasi from '@/formulir/tahap/TahapKonfirmasi';
import type { Jurusan, Tahap } from '@/tipe/model';

interface Props {
    jurusans: Jurusan[];
    tahunLulus: number;
    stempel: string;
    tandaTangan: string;
    maksBerkasMb: number;
    lahirPaling: { awal: string; akhir: string };
    tahapan: Tahap[];
    kategoriPembawa: Record<string, string>;
    kategoriWajibInstansi: string;
    kategoriWajibKelas: string;
    nisnWajib: boolean;
    judul: string;
    deskripsi: string;
}

function token(): string {
    return document.querySelector<HTMLMetaElement>('meta[name="csrf-token"]')?.content ?? '';
}

/** Tanggal hari ini sebagai 'YYYY-MM-DD' menurut zona waktu perangkat. */
function tanggalHariIni(): string {
    const d = new Date();
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}

/** Penanda kapan daftar periksa terakhir dibuka. */
const KUNCI_PERIKSA = 'spmb.periksa-terakhir';

/**
 * Nomor dan token pendaftaran yang baru terbit. Disimpan terpisah dari isian
 * formulir karena keduanya berasal dari server. Tanpa ini, refresh di langkah
 * selesai menghapus nomor dari layar padahal pendaftarannya sudah tersimpan.
 */
const KUNCI_PENDAFTARAN = 'spmb-pendaftaran';

interface PendaftaranBerjalan {
    nomor: string;
    token: string;
}

function bacaPendaftaran(): PendaftaranBerjalan | null {
    try {
        const mentah = sessionStorage.getItem(KUNCI_PENDAFTARAN);
        if (!mentah) return null;

        const isi = JSON.parse(mentah) as Partial<PendaftaranBerjalan>;

        return isi.nomor && isi.token ? { nomor: isi.nomor, token: isi.token } : null;
    } catch {
        return null;
    }
}

function simpanPendaftaran(berjalan: PendaftaranBerjalan | null): void {
    try {
        if (berjalan) {
            sessionStorage.setItem(KUNCI_PENDAFTARAN, JSON.stringify(berjalan));
        } else {
            sessionStorage.removeItem(KUNCI_PENDAFTARAN);
        }
    } catch {
        // Mode privat dapat melarang sessionStorage; bukan galat fatal.
    }
}

async function kirimJson(
    url: string,
    data: Record<string, unknown>,
): Promise<Record<string, unknown>> {
    const r = await fetch(url, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Accept: 'application/json',
            'X-CSRF-TOKEN': token(),
            'X-Requested-With': 'XMLHttpRequest',
        },
        body: JSON.stringify(data),
    });
    const isi = (await r.json()) as Record<string, unknown>;
    if (!r.ok) throw { status: r.status, isi };
    return isi;
}

export default function Daftar({
    jurusans,
    tahunLulus,
    stempel,
    tandaTangan,
    maksBerkasMb,
    lahirPaling,
    tahapan,
    kategoriPembawa,
    kategoriWajibInstansi,
    kategoriWajibKelas,
    nisnWajib,
    judul,
}: Props) {
    const jumlahTahap = tahapan.length;
    const formRef = useRef<HTMLFormElement>(null);
    const simpanOtomatis = useSimpanOtomatis();

    const [langkah, setLangkah] = useState(1);
    const [tanggalLahir, setTanggalLahir] = useState('');
    const [nikPanjang, setNikPanjang] = useState(0);
    const [nisnPanjang, setNisnPanjang] = useState(0);
    const [waPanjang, setWaPanjang] = useState(0);
    const [caraBerkas, setCaraBerkas] = useState('unggah');
    const [kategoriPembawaTerpilih, setKategoriPembawaTerpilih] = useState('');
    const [nomorDaftar, setNomorDaftar] = useState('');
    const [tokenBukti, setTokenBukti] = useState('');
    const [mengunggahBerkas, setMengunggahBerkas] = useState(false);
    const [sedangKirim, setSedangKirim] = useState(false);
    const [tampilLapisan, setTampilLapisan] = useState(false);

    const maksBita = maksBerkasMb * 1024 * 1024;
    const unggah = useUnggahBerkas({
        url: '/daftar/berkas',
        maksBita,
    });

    // Konversi keadaan hook ke format KartuUnggah
    function keKartu(k: { status: StatusUnggah; kemajuan: number; pesan: string }): KeadaanBerkas {
        const keterangan = `JPG, PNG, atau PDF · maks ${maksBerkasMb} MB`;
        return {
            rincian: k.pesan || keterangan,
            aksi:
                k.status === 'selesai' ? '✓' : k.status === 'mengirim' ? `${k.kemajuan}%` : 'Pilih',
            tampilBatang: k.status === 'mengirim',
            kemajuan: k.kemajuan,
            penanda: k.status === 'selesai' ? 'ok' : k.status === 'galat' ? 'galat' : '',
            status: k.pesan,
        };
    }

    // Pulihkan isian dari sessionStorage. Yang tersimpan hanya isian teks:
    // berkas tidak pernah disimpan di peramban, jadi bila pendaftar berada di
    // langkah 5 ia perlu memilih berkasnya lagi.
    useEffect(() => {
        if (!formRef.current) return;

        // Nomor dan token diambil lebih dulu dan berdiri sendiri: kotak nomor
        // di langkah selesai hanya tampil bila keduanya ada, termasuk setelah
        // refresh.
        const berjalan = bacaPendaftaran();
        if (berjalan) {
            setNomorDaftar(berjalan.nomor);
            setTokenBukti(berjalan.token);
        }

        const dipulihkan = simpanOtomatis.pulihkan(formRef.current);
        if (dipulihkan) {
            // Mengisi value lewat kode tidak memicu 'input', jadi penghitung
            // digit disegarkan dari kolomnya langsung.
            const panjang = (nama: string) =>
                formRef.current?.querySelector<HTMLInputElement>(`[name="${nama}"]`)?.value
                    .length ?? 0;
            setNikPanjang(panjang('nik'));
            setNisnPanjang(panjang('nisn'));
            setWaPanjang(panjang('whatsapp'));
            // Kolom yang dikendalikan React ikut disegarkan; tanpa ini render
            // pertama menimpa DOM dengan nilai bawaan.
            setTanggalLahir(dipulihkan.tanggal_lahir ?? '');
            setCaraBerkas(dipulihkan.cara_berkas === 'sekolah' ? 'sekolah' : 'unggah');
            setKategoriPembawaTerpilih(dipulihkan.kategori_pembawa ?? '');
            // Seluruh langkah aman dipulihkan: tidak ada yang bergantung pada
            // data server. Berkas yang tadi dipilih tetap perlu dipilih ulang.
            const lk = simpanOtomatis.langkahTerakhir(6);
            if (lk > 1) pindahKe(lk, false);
        } else if (berjalan) {
            // Isian sudah dibersihkan setelah pendaftaran terkirim, tetapi
            // nomornya masih tersimpan: pendaftar tetap dibawa ke langkah
            // selesai agar nomornya terlihat.
            pindahKe(6, false);
        }
    }, []);

    /*
     * Daftar periksa cukup muncul sekali sehari. Penandanya bernilai tanggal,
     * bukan angka, jadi penyimpanan lama tetap sah tanpa perlu dihitung ulang.
     * Tandanya baru ditulis saat modal ditutup, supaya pemilihan komponen
     * ulang (StrictMode) tidak menganggap modal sudah dibuka.
     */
    useEffect(() => {
        try {
            if (localStorage.getItem(KUNCI_PERIKSA) === tanggalHariIni()) return;
        } catch {
            // Penyimpanan tidak tersedia; modal tampil tiap kunjungan.
        }
        setTampilLapisan(true);
    }, []);

    const tutupLapisan = () => {
        setTampilLapisan(false);
        try {
            localStorage.setItem(KUNCI_PERIKSA, tanggalHariIni());
        } catch {
            // Abaikan: modal hanya akan tampil lagi pada kunjungan berikutnya.
        }
    };

    // Pantau perubahan isian NIK dan NISN
    useEffect(() => {
        const form = formRef.current;
        if (!form) return;
        function saatInput(e: Event) {
            const el = e.target as HTMLInputElement;
            if (el.name === 'nik') setNikPanjang(el.value.length);
            if (el.name === 'nisn') setNisnPanjang(el.value.length);
            if (el.name === 'whatsapp') setWaPanjang(el.value.length);
        }
        form.addEventListener('input', saatInput);
        return () => form.removeEventListener('input', saatInput);
    }, []);

    function pindahKe(ke: number, gulir = true) {
        setLangkah(ke);
        const t = tahapan[ke - 1];
        if (t) umumkan(`Langkah ${ke} dari ${jumlahTahap}: ${t.nama}`);
        if (gulir) {
            window.scrollTo({ top: 0, behavior: 'smooth' });
        }
        if (formRef.current) {
            simpanOtomatis.simpan(formRef.current);
            simpanOtomatis.simpanLangkah(ke);
        }
    }

    /**
     * Langkah 1-5 hanya berjalan di peramban: isian disimpan ke sessionStorage,
     * berkas ditahan di memori. Tidak ada yang menyentuh server sampai
     * pendaftar menekan tombol kirim di langkah 6.
     */
    async function lanjut() {
        const form = formRef.current;
        if (!form) return;

        const tahapEl = form.querySelector(`[data-tahap="${langkah}"]`);
        if (tahapEl && !(await tahapSah(tahapEl))) return;

        // Berkas diminta lebih dulu bila memilih unggah daring; tanpa ini
        // pendaftar baru sadar setelah datanya terlanjur tersimpan.
        if (langkah === 5 && caraBerkas === 'unggah' && !unggah.lengkap()) {
            await pesan({
                icon: 'warning',
                title: 'Berkas belum lengkap',
                text: 'Pilih ketiga berkas (KK, Akta Kelahiran, KTP orang tua), atau pilih "Ke sekolah" bila akan membawanya saat daftar ulang.',
                confirmButtonText: 'Mengerti',
            });

            return;
        }

        simpanOtomatis.simpan(form);
        pindahKe(langkah + 1);
    }

    /**
     * Langkah terakhir adalah satu-satunya titik simpan: data teks dan
     * persetujuan dikirim bersama, nomor terbit, lalu berkas menyusul.
     */
    async function selesaikan() {
        const form = formRef.current;
        if (!form) return;

        const tahapEl = form.querySelector(`[data-tahap="${langkah}"]`);
        if (tahapEl && !(await tahapSah(tahapEl))) return;

        const ya = await tanya({
            title: 'Kirim pendaftaran?',
            text: 'Setelah dikirim, nomor pendaftaran Anda terbit dan data tidak dapat diubah sendiri. Hubungi panitia bila ada yang perlu diperbaiki.',
            confirmButtonText: 'Ya, kirim',
            cancelButtonText: 'Periksa lagi',
        });

        if (!ya) return;

        setSedangKirim(true);
        try {
            const data = Object.fromEntries(new FormData(form));
            const hasil = await kirimJson('/daftar', data as Record<string, string>);
            const nomor = hasil.nomor as string;
            const token = hasil.token as string;

            // Nomor langsung ditampilkan di langkah ini supaya pendaftar
            // sempat mencatatnya sebelum berpindah ke halaman bukti. Ikut
            // disimpan ke sessionStorage agar refresh tidak menghapusnya.
            setNomorDaftar(nomor);
            setTokenBukti(token);
            simpanPendaftaran({ nomor, token });
            umumkan(`Pendaftaran tersimpan. Nomor pendaftaran Anda ${nomor}.`);

            // Berkas menyusul setelah nomor terbit. Kegagalan unggah tidak
            // membatalkan pendaftaran: berkas bisa diunggah ulang dari cek status.
            if (caraBerkas === 'unggah') {
                setMengunggahBerkas(true);
                await unggah.unggahSemua(nomor, token);
                setMengunggahBerkas(false);
            }

            // Isian sengaja tidak dihapus: pendaftar masih di langkah selesai
            // dan boleh refresh — rekapan serta nomor tetap utuh. Keduanya
            // baru dibersihkan saat halaman bukti dibuka.
        } catch (e) {
            const galat = e as {
                status?: number;
                isi?: { message?: string; nik_terdaftar?: { nama: string; sekolah_asal: string } };
            };

            if (galat.isi?.nik_terdaftar) {
                await pesan({
                    icon: 'warning',
                    title: 'NIK sudah terdaftar',
                    html: `<p>NIK ini sudah dipakai oleh <b>${galat.isi.nik_terdaftar.nama}</b> dari <b>${galat.isi.nik_terdaftar.sekolah_asal}</b>.</p><p>Silakan cek status pendaftaran Anda.</p>`,
                    confirmButtonText: 'Mengerti',
                });
            } else {
                await pesan({
                    icon: 'error',
                    title: 'Gagal mengirim',
                    text: galat.isi?.message ?? 'Terjadi kesalahan. Coba lagi.',
                });
            }
        } finally {
            setSedangKirim(false);
        }
    }

    /** Membuka halaman bukti setelah pendaftar mencatat nomornya. */
    function keBukti() {
        if (!nomorDaftar) return;

        // Formulir selesai: isian dan nomor tidak perlu dipulihkan lagi.
        simpanPendaftaran(null);
        simpanOtomatis.hapus();
        window.location.href = `/daftar/bukti/${nomorDaftar}?t=${encodeURIComponent(tokenBukti)}`;
    }

    function kembali() {
        if (langkah > 1) pindahKe(langkah - 1);
    }

    function keRekap(): {
        diri: string;
        alamat: string;
        sekolah: string;
        jurusan: string;
        orangTua: string;
        pembawa: string;
        berkas: string;
    } {
        const form = formRef.current;
        if (!form)
            return {
                diri: '',
                alamat: '',
                sekolah: '',
                jurusan: '',
                orangTua: '',
                pembawa: '',
                berkas: '',
            };
        const d = new FormData(form);
        const v = (k: string) => (d.get(k) as string) ?? '';

        const jurusanTerpilih = jurusans.find((j) => j.kode === v('jurusan1'));
        const jenisKelamin = v('jenis_kelamin') === 'L' ? 'Laki-laki' : 'Perempuan';

        return {
            // Nama (jenis kelamin) - NIK
            diri: [v('nama') ? `${v('nama')} (${jenisKelamin})` : '', v('nik')]
                .filter(Boolean)
                .join(' - '),
            alamat: [
                v('jalan'),
                v('dukuh'),
                `RT ${v('rt')} / RW ${v('rw')}`,
                v('desa'),
                v('kecamatan'),
                v('kabupaten'),
                v('provinsi'),
            ]
                .filter(Boolean)
                .join(', '),
            // Nama sekolah - tahun lulus
            sekolah: [v('sekolah_asal'), v('tahun_lulus')].filter(Boolean).join(' - '),
            jurusan: jurusanTerpilih?.nama ?? v('jurusan1'),
            // Pembawa berdiri sendiri, tidak digabung dengan data orang tua.
            orangTua: [`Ayah: ${v('ayah')}`, `Ibu: ${v('ibu')}`].filter(Boolean).join(' · '),
            pembawa: v('pembawa'),
            berkas: caraBerkas === 'sekolah' ? 'Dibawa ke sekolah' : 'Diunggah',
        };
    }

    // Stepper untuk hero
    const kemajuan = (
        <ol className="stepper" aria-label="Tahap pendaftaran" data-stepper>
            {tahapan.map((t, i) => (
                <li
                    key={i}
                    className={i < langkah ? (i === langkah - 1 ? 'aktif' : 'selesai') : ''}
                    {...(i === langkah - 1 ? { 'aria-current': 'step' as const } : {})}
                >
                    <span>{t.ringkas}</span>
                </li>
            ))}
        </ol>
    );

    // Langkah 1-5 tidak mengirim apa pun; hanya langkah 6 yang menyimpan.
    const tombolUtama = nomorDaftar ? (
        // Nomor sudah terbit: tombol berpindah menjadi jalan ke bukti.
        <button
            className="tombol utama"
            type="button"
            onClick={keBukti}
            disabled={mengunggahBerkas}
        >
            <span>{mengunggahBerkas ? 'Mengunggah berkas…' : 'Lihat bukti pendaftaran'}</span>{' '}
            <Ikon nama="arrow" />
        </button>
    ) : langkah === 6 ? (
        <button className="tombol utama" type="button" onClick={selesaikan} disabled={sedangKirim}>
            <span>{sedangKirim ? 'Mengirim…' : 'Kirim pendaftaran'}</span> <Ikon nama="check" />
        </button>
    ) : (
        <button className="tombol utama" type="button" onClick={lanjut} disabled={sedangKirim}>
            <span>Berikutnya</span> <Ikon nama="arrow" />
        </button>
    );

    return (
        <TataLetakPublik fokus tahapan={tahapan} wadah="wadah--formulir" kemajuan={kemajuan}>
            <Head title={judul} />

            {/* Modal daftar periksa */}
            {tampilLapisan && (
                <div
                    className="lapisan"
                    role="dialog"
                    aria-modal="true"
                    aria-labelledby="judul-periksa"
                >
                    <div className="lembar">
                        <div className="pegangan"></div>
                        <p className="eyebrow" style={{ color: '#8a6d12' }}>
                            Sebelum mulai
                        </p>
                        <h2 id="judul-periksa">Siapkan berkasmu</h2>
                        <p>Pastikan dokumen berikut tersedia dan terlihat jelas.</p>
                        <ul className="periksa">
                            <li>
                                <i>1</i>Kartu Keluarga (KK)
                            </li>
                            <li>
                                <i>2</i>Akta Kelahiran
                            </li>
                            <li>
                                <i>3</i>KTP orang tua
                            </li>
                            <li className="info">
                                <i>i</i>Format JPG, PNG, atau PDF · maksimal {maksBerkasMb} MB per
                                berkas
                            </li>
                        </ul>
                        <button className="tombol utama" type="button" onClick={tutupLapisan}>
                            Saya siap, mulai mengisi <Ikon nama="arrow" />
                        </button>
                    </div>
                </div>
            )}

            <div className="tata">
                <form
                    className="kartu kartu-form"
                    ref={formRef}
                    noValidate
                    autoComplete="off"
                    data-pendaftaran
                    onSubmit={(e) => e.preventDefault()}
                >
                    <input type="hidden" name="form_stempel" value={stempel} />
                    <input type="hidden" name="form_tanda_tangan" value={tandaTangan} />

                    {/* Honeypot */}
                    <div className="sawar" aria-hidden="true">
                        <label htmlFor="alamat-surat">Jangan isi kolom ini</label>
                        <input
                            id="alamat-surat"
                            name="alamat_surat"
                            type="text"
                            tabIndex={-1}
                            autoComplete="off"
                        />
                    </div>

                    {/* Tahap-tahap */}
                    <div style={langkah !== 1 ? { display: 'none' } : undefined}>
                        <TahapDataDiri
                            tahap={tahapan[0]!}
                            nomor={1}
                            total={jumlahTahap}
                            nisnWajib={nisnWajib}
                            lahirMin={lahirPaling.awal}
                            lahirMaks={lahirPaling.akhir}
                            tanggalLahir={tanggalLahir}
                            onTanggalLahirChange={setTanggalLahir}
                            nikPanjang={nikPanjang}
                            nisnPanjang={nisnPanjang}
                            waPanjang={waPanjang}
                        />
                    </div>
                    <div style={langkah !== 2 ? { display: 'none' } : undefined}>
                        <TahapAlamat tahap={tahapan[1]!} nomor={2} total={jumlahTahap} />
                    </div>
                    <div style={langkah !== 3 ? { display: 'none' } : undefined}>
                        <TahapSekolah
                            tahap={tahapan[2]!}
                            nomor={3}
                            total={jumlahTahap}
                            jurusans={jurusans}
                            tahunLulus={tahunLulus}
                        />
                    </div>
                    <div style={langkah !== 4 ? { display: 'none' } : undefined}>
                        <TahapOrangTua
                            tahap={tahapan[3]!}
                            nomor={4}
                            total={jumlahTahap}
                            kategoriPembawa={kategoriPembawa}
                            kategoriWajibInstansi={kategoriWajibInstansi}
                            kategoriWajibKelas={kategoriWajibKelas}
                            kategori={kategoriPembawaTerpilih}
                            onKategoriChange={setKategoriPembawaTerpilih}
                        />
                    </div>
                    <div style={langkah !== 5 ? { display: 'none' } : undefined}>
                        <TahapBerkas
                            tahap={tahapan[4]!}
                            nomor={5}
                            total={jumlahTahap}
                            maksBerkasMb={maksBerkasMb}
                            keadaanKk={keKartu(unggah.keadaan.kk)}
                            keadaanAkta={keKartu(unggah.keadaan.akta)}
                            keadaanKtp={keKartu(unggah.keadaan.ktp)}
                            onPilihBerkas={(j, f) => unggah.pilih(j as 'kk' | 'akta' | 'ktp', f)}
                            caraBerkas={caraBerkas}
                            onCaraBerkasChange={setCaraBerkas}
                        />
                    </div>
                    <div style={langkah !== 6 ? { display: 'none' } : undefined}>
                        <TahapKonfirmasi
                            tahap={tahapan[5]!}
                            nomor={6}
                            total={jumlahTahap}
                            rekap={keRekap()}
                            onKeTahap={pindahKe}
                            nomorDaftar={nomorDaftar}
                        />
                    </div>

                    {/* Tombol aksi desktop */}
                    <div className="aksi-baris dalam">
                        {langkah > 1 && (
                            <button className="tombol kedua" type="button" onClick={kembali}>
                                <Ikon nama="back" /> Sebelumnya
                            </button>
                        )}
                        {tombolUtama}
                    </div>
                </form>

                <aside className="sisi">
                    <div className="kartu kartu-sisi langkah">
                        <h3>Progres pendaftaran</h3>
                        <p>Sekitar 5 menit untuk menyelesaikan.</p>
                        <ol className="daftar-langkah">
                            {tahapan.map((t, i) => (
                                <li
                                    key={i}
                                    className={
                                        i < langkah ? (i === langkah - 1 ? 'aktif' : 'selesai') : ''
                                    }
                                    {...(i === langkah - 1
                                        ? { 'aria-current': 'step' as const }
                                        : {})}
                                >
                                    <i>{i + 1}</i>
                                    {t.ringkas}
                                </li>
                            ))}
                        </ol>
                    </div>
                    <p className="aman">
                        <Ikon nama="lock" ukuran={14} /> Data dilindungi sesuai UU 27/2022
                    </p>
                </aside>
            </div>

            {/* Bilah aksi bawah (ponsel) */}
            <div className="dok">
                <div className="dok-info">
                    <span>
                        Langkah <b>{langkah}</b> dari {jumlahTahap}
                    </span>
                    <span>± 5 menit lagi</span>
                </div>
                <div className="aksi-baris">
                    {langkah > 1 && (
                        <button
                            className="tombol kedua"
                            type="button"
                            onClick={kembali}
                            aria-label="Sebelumnya"
                        >
                            <Ikon nama="back" />
                        </button>
                    )}
                    {tombolUtama}
                </div>
            </div>

            {/* Tombol bantuan mengambang */}
            <a
                className="bantu-apung"
                href="/cek-status"
                title="Butuh bantuan? Chat panitia via WhatsApp"
                aria-label="Butuh bantuan? Chat panitia via WhatsApp"
            >
                <Ikon nama="wa" ukuran={26} />
            </a>
        </TataLetakPublik>
    );
}
