import React, { useEffect, useMemo, useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { logApiError } from "../utils/logger";
import { toastError, toastInfo } from "../utils/toastBus";
import { createShow, deleteShow, fetchDanimeEpisode, fetchShows, postCurrentEpisode } from "../api/watchparty";
import type { DanimeEpisode, ShowItem } from "../api/watchparty";
import { useAuth } from "../auth/AuthProvider";

const REDIRECT_DELAY_S = 3;

const HHMM = /^(\d{1,2}):([0-5]\d)$/;
const HHMMSS_OPT = /^(\d{1,2}):([0-5]\d)(?::([0-5]\d))?$/;

function toHHMMSS(v: string): string | null {
    const m = v.trim().match(HHMM);
    if (!m) return null;
    const h = Math.abs(parseInt(m[1], 10));
    if (h > 23) return null;
    const hh = String(h).padStart(2, "0");
    const mm = m[2];
    return `${hh}:${mm}:00`;
}
function toHHMM(v: string): string | null {
    const m = v.trim().match(/^(\d{1,2}):([0-5]\d)$/);
    if (!m) return null;
    const h = Number(m[1]); if (h > 23) return null;
    return `${String(h).padStart(2, "0")}:${m[2]}`;
}

function normalizeStartTimeInput(v: string): string | null {
    const t = v.trim();
    if (!t) return "";
    const m = t.match(HHMMSS_OPT);
    if (!m) return null;
    const h = Number(m[1]);
    if (h > 23) return null;
    const mm = m[2];
    const ss = m[3] ?? "00";
    return `${String(h).padStart(2, "0")}:${mm}:${ss}`;
}

function extractPartId(urlStr: string): string | null {
    const trimmed = urlStr.trim();
    if (!trimmed) return null;
    try {
        const u = new URL(trimmed);
        if (!u.hostname.endsWith("animestore.docomo.ne.jp")) return null;
        const partId = u.searchParams.get("partId") || u.searchParams.get("partid");
        if (partId) return partId;
    } catch {
        // fallback to regex
    }
    if (!trimmed.includes("animestore.docomo.ne.jp")) return null;
    const m = trimmed.match(/partId=(\d+)/i);
    return m ? m[1] : null;
}

export default function ShowsPage() {
    const [shows, setShows] = useState<ShowItem[]>([]);
    const [loading, setLoading] = useState(true);
    const [posting, setPosting] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const [redirectIn, setRedirectIn] = useState<number | null>(null);
    const [deletingId, setDeletingId] = useState<number | null>(null);
    const redirectTid = useRef<number | null>(null);
    const isRedirecting = redirectIn !== null;
    const navigate = useNavigate();
    const { enabled: authEnabled, idToken, backendClaims, verifying } = useAuth();
    const isAuthed = authEnabled && !!idToken;

    // フォーム状態
    const [selectedId, setSelectedId] = useState<number | null>(null);
    const [startTime, setStartTime] = useState("");
    const [danimeUrl, setDanimeUrl] = useState("");
    const [danimeStart, setDanimeStart] = useState("");
    const [danimeResult, setDanimeResult] = useState<DanimeEpisode | null>(null);
    const [scraping, setScraping] = useState(false);
    const [registeringScrape, setRegisteringScrape] = useState(false);
    const [scrapeError, setScrapeError] = useState<string | null>(null);

    function formatPlaybackLen(v: string): string {
        const parts = v.split(":").map(Number);
        if (parts.length === 2) {
            // already MM:SS
            const [mm, ss] = parts;
            return `${String(mm).padStart(2, "0")}:${String(ss).padStart(2, "0")}`;
        }
        if (parts.length === 3) {
            const [hh, mm, ss] = parts;
            if (hh <= 0) {
                // show MM:SS when hours are zero
                return `${String(mm).padStart(2, "0")}:${String(ss).padStart(2, "0")}`;
            }
            // show HH:MM:SS when hours exist
            return `${String(hh).padStart(2, "0")}:${String(mm).padStart(2, "0")}:${String(ss).padStart(2, "0")}`;
        }
        return v; // fallback (unexpected format)
    }

    const loadShows = React.useCallback(async () => {
        setError(null);
        try {
            setLoading(true);
            const data = await fetchShows();
            data.sort((a, b) => a.id - b.id);   // ASC
            setShows(data);
        } catch (e: unknown) {
            const msg = e instanceof Error ? e.message : "番組一覧を取得できませんでした。";
            setError(msg);
            logApiError("fetch shows", e);
            toastError("番組一覧を取得できませんでした", msg);
        } finally {
            setLoading(false);
        }
    }, []);

    // 一覧取得
    useEffect(() => {
        loadShows().catch(() => {});
    }, [loadShows]);
    useEffect(() => {
        return () => {
            if (redirectTid.current) {
                window.clearInterval(redirectTid.current);
            }
        };
    }, []);

    const current = useMemo(() => shows.find(s => s.id === selectedId) || null, [shows, selectedId]);

    async function submit() {
        setError(null);
        if (!selectedId) { setError("エピソードを選択してください。"); return; }

        const selected = shows.find(s => s.id === selectedId);
        if (!selected) { setError("選択中のエピソードが見つかりませんでした。"); return; }

        const payload: { id: number; start_time?: string } = { id: selectedId };

        if (startTime.trim()) {
            const normalized = toHHMMSS(startTime);
            if (!normalized) { setError("開始時刻は HH:MM 形式で入力してください。"); return; }
            payload.start_time = normalized;
        } else {
            payload.start_time = selected.start_time;
        }

        try {
            setPosting(true);
            await postCurrentEpisode(payload);
            toastInfo("現在のエピソードを設定しました");
            setRedirectIn(REDIRECT_DELAY_S);
            const start = Date.now();
            redirectTid.current = window.setInterval(() => {
                const left = REDIRECT_DELAY_S - Math.floor((Date.now() - start) / 1000);
                if (left <= 0) { window.clearInterval(redirectTid.current!); navigate("/", { replace: true }); }
                else setRedirectIn(left);
            }, 250);
        } catch (e: unknown) {
            const msg = e instanceof Error ? e.message : "現在のエピソードを設定できませんでした。";
            setError(msg);
            logApiError("set current episode", e);
            toastError("現在のエピソードを設定できませんでした", msg);
        } finally {
            setPosting(false);
        }
    }

    async function handleScrape() {
        setScrapeError(null);
        setDanimeResult(null);
        const partId = extractPartId(danimeUrl);
        if (!partId) {
            setScrapeError("animestore.docomo.ne.jp のエピソードURL（partId付き）を入力してください。");
            return;
        }
        try {
            setScraping(true);
            const res = await fetchDanimeEpisode(partId);
            setDanimeResult(res);
            toastInfo("メタデータを取得しました");
        } catch (e: unknown) {
            const msg = e instanceof Error ? e.message : "メタデータ取得に失敗しました。";
            setScrapeError(msg);
            logApiError("fetch danime", e);
            toastError("メタデータ取得に失敗しました", msg);
        } finally {
            setScraping(false);
        }
    }

    async function handleRegisterScraped() {
        if (!danimeResult) {
            setScrapeError("先にメタデータを取得してください。");
            return;
        }
        const normalized = normalizeStartTimeInput(danimeStart);
        if (normalized === null) {
            setScrapeError("開始時刻は HH:MM または HH:MM:SS で入力してください。");
            return;
        }

        try {
            setRegisteringScrape(true);
            await createShow({
                ep_num: danimeResult.ep_num,
                ep_title: danimeResult.ep_title,
                season_name: danimeResult.season_name,
                playback_length: danimeResult.playback_length,
                start_time: normalized || undefined,
            });
            toastInfo("番組を登録しました");
            setDanimeStart("");
            setDanimeUrl("");
            setDanimeResult(null);
            await loadShows();
        } catch (e: unknown) {
            const msg = e instanceof Error ? e.message : "登録に失敗しました。";
            setScrapeError(msg);
            logApiError("register show", e);
            toastError("登録に失敗しました", msg);
        } finally {
            setRegisteringScrape(false);
        }
    }

    async function handleDelete(showId: number) {
        if (!authEnabled) {
            toastError("認証が無効です", "管理者に確認してください");
            return;
        }
        if (!idToken) {
            toastError("サインインしてください", "Googleでサインイン後に削除できます");
            return;
        }
        try {
            setDeletingId(showId);
            await deleteShow(showId, idToken);
            toastInfo("エピソードを削除しました");
            if (selectedId === showId) {
                setSelectedId(null);
            }
            await loadShows();
        } catch (e: unknown) {
            const msg = e instanceof Error ? e.message : "削除に失敗しました。";
            toastError("削除に失敗しました", msg);
            logApiError("delete show", e);
        } finally {
            setDeletingId(null);
        }
    }

    return (
        <div className="shows-page">
            <div className="scrape-card">
                <h2 className="h1" style={{ marginBottom: 8 }}>dアニメから登録</h2>
                <p className="subtle" style={{ marginTop: 0 }}>
                    dアニメのエピソードURLを貼り付けて「メタデータ取得」を押すと、話数・タイトル・再生時間を取得します。
                    開始時刻だけ任意で入力できます（空なら未指定で送信）。
                </p>
                <div className="scrape-row">
                    <input
                        className="input"
                        type="url"
                        placeholder="https://animestore.docomo.ne.jp/animestore/ci_pc?workId=...&partId=..."
                        value={danimeUrl}
                        onChange={e => setDanimeUrl(e.target.value)}
                        style={{ flex: 1, minWidth: 260 }}
                    />
                    <button className="primary" disabled={scraping || !danimeUrl.trim()} onClick={handleScrape}>
                        {scraping ? "取得中…" : "メタデータ取得"}
                    </button>
                </div>
                {danimeResult && (
                    <div className="scrape-result">
                        <div className="scrape-meta">
                            <div className="pill">第{danimeResult.ep_num}話</div>
                            <div className="pill">{danimeResult.playback_length}</div>
                        </div>
                        <div className="scrape-title">「{danimeResult.ep_title}」</div>
                        <div className="subtle" style={{ margin: "4px 0 12px" }}>{danimeResult.season_name}</div>
                        <div className="scrape-row">
                            <input
                                className="input"
                                type="text"
                                inputMode="numeric"
                                placeholder="開始時刻 (HH:MM または HH:MM:SS、空欄可)"
                                value={danimeStart}
                                onChange={e => setDanimeStart(e.target.value)}
                                onBlur={e => {
                                    const v = normalizeStartTimeInput(e.target.value);
                                    if (v) setDanimeStart(v);
                                }}
                                style={{ flex: 1, minWidth: 220 }}
                            />
                            <button
                                className="primary"
                                disabled={registeringScrape}
                                onClick={handleRegisterScraped}
                            >
                                {registeringScrape ? "登録中…" : "このデータで登録"}
                            </button>
                        </div>
                    </div>
                )}
                {scrapeError && (
                    <div className="timer-status" style={{ color: "#f88" }}>
                        {scrapeError}
                    </div>
                )}
            </div>

            <h2 className="h1" style={{ marginBottom: 8 }}>エピソード一覧</h2>
            <div className="subtle" style={{ marginTop: 0, display: "flex", flexDirection: "column", gap: 4 }}>
                <span>エピソードを選び、必要に応じて開始時刻（HH:MM）を入力し、「現在のエピソードに設定」を押してください。</span>
                <span>
                    削除は認証後のみ実行できます。サイドバー下部の「管理用サインイン」からログインしてください。
                    {isAuthed && (
                        <span style={{ color: backendClaims ? "#6de3a2" : "#f0d000" }}>
                            {" "} {verifying ? "トークン確認中…" : backendClaims ? "バックエンドで確認済み" : "未確認トークン"}
                        </span>
                    )}
                </span>
            </div>

            {loading && <div className="subtle">読み込み中…</div>}
            {error && (
                <div className="timer-status" style={{ color: "#f88" }}>
                    {error}{" "}
                    <button className="link-btn" onClick={() => loadShows()}>
                        再試行
                    </button>
                </div>
            )}

            {!loading && shows.length === 0 && <div className="subtle">エピソードがありません。</div>}

            {shows.length > 0 && (
                <div className="shows-grid">
                    {shows.map(s => (
                        <button
                            key={s.id}
                            className={`show-card ${selectedId === s.id ? "selected" : ""}`}
                            onClick={() => {
                                setSelectedId(s.id);
                                setStartTime(s.start_time.slice(0, 5));
                            }}
                        >
                            <div className="title-row">
                                <div className="title">第{s.ep_num}話：{s.ep_title}</div>
                                <button
                                    className="link-btn danger"
                                    onClick={(e) => { e.stopPropagation(); handleDelete(s.id).catch(() => {}); }}
                                    disabled={deletingId === s.id || verifying}
                                    title="このエピソードを削除（認証が必要）"
                                >
                                    {deletingId === s.id ? "削除中…" : "削除"}
                                </button>
                            </div>
                            <div className="season subtle">{s.season_name}</div>
                            <div className="meta subtle">
                                開始時刻 {s.start_time.slice(0, 5)}・再生時間 {formatPlaybackLen(s.playback_length)}
                            </div>
                        </button>
                    ))}
                </div>
            )}

            <div className="form-row">
                <input
                    className="input"
                    type="text"
                    inputMode="numeric"
                    placeholder="HH:MM"
                    value={startTime}
                    onChange={(e) => setStartTime(e.target.value)}
                    onBlur={(e) => {
                        const v = toHHMM(e.target.value);
                        if (v) setStartTime(v);
                    }}
                />
                <button
                    className="primary"
                    disabled={
                        posting ||
                        !selectedId ||
                        isRedirecting ||
                        (!!startTime && !toHHMMSS(startTime))
                    }
                    onClick={submit}
                >
                    {redirectIn !== null ? `設定しました（${redirectIn}）` : posting ? "設定中…" : "現在のエピソードに設定"}
                </button>
            </div>

            {current && (
                <div className="subtle" style={{ marginTop: 8 }}>
                    選択中：第{current.ep_num}話「{current.ep_title}」
                </div>
            )}
        </div>
    );
}
