import base64
import secrets
import time
from datetime import datetime, timedelta
from pathlib import Path

from fastapi import APIRouter, HTTPException, BackgroundTasks
from fastapi.responses import FileResponse
from pydantic import BaseModel

from app.modules.egov.state import state, EGovStatus
from app.modules.egov.scraper import run_login, run_download
import app.modules.egov.egov_api as egov_api

router = APIRouter(prefix="/egov", tags=["e-Gov"])


class LoginRequest(BaseModel):
    user_id:  str
    password: str


class MfaRequest(BaseModel):
    mfa_code: str


class CallbackRequest(BaseModel):
    code:  str
    state: str


class ApplyRequest(BaseModel):
    proc_id: str  # 16文字の手続識別子


class ConfigRequest(BaseModel):
    software_id:  str
    api_key:      str
    redirect_uri: str = "http://192.168.0.61:18000/egov.html"


@router.get("/status")
def get_status():
    import os
    return {
        "status":        state.status,
        "message":       state.message,
        "screenshot":    state.screenshot_b64,
        "last_download": state.last_download,
        "token_valid":   state.is_token_valid(),
        "environment":   os.environ.get("EGOV_ENV", "production"),
    }


@router.post("/login-start")
async def login_start(req: LoginRequest, background_tasks: BackgroundTasks):
    if state.status in (EGovStatus.STARTING, EGovStatus.WAITING_MFA):
        raise HTTPException(status_code=409, detail="ログイン処理が既に実行中です")

    await state.close_browser()
    state.reset()
    background_tasks.add_task(run_login, req.user_id, req.password)
    return {"message": "ログイン処理を開始しました"}


@router.post("/login-mfa")
async def login_mfa(req: MfaRequest):
    if state.status != EGovStatus.WAITING_MFA:
        raise HTTPException(status_code=409, detail="MFA 入力待ち状態ではありません")
    if not req.mfa_code.isdigit() or len(req.mfa_code) != 6:
        raise HTTPException(status_code=400, detail="認証コードは 6 桁の数字です")

    state.mfa_code = req.mfa_code
    state.mfa_event.set()
    return {"message": "認証コードを送信しました"}


@router.post("/logout")
async def logout():
    # API トークンがあればサーバー側でも無効化
    if state.refresh_token:
        try:
            cfg = egov_api.load_api_config()
            await egov_api.do_logout(
                state.refresh_token,
                cfg["client_id"],
                cfg["client_secret"],
            )
        except Exception:
            pass
    egov_api.delete_tokens()
    await state.close_browser()
    state.reset()
    cookies = Path("/app/egov_data/cookies.json")
    if cookies.exists():
        cookies.unlink()
    return {"message": "ログアウトしました"}


@router.post("/download-start")
async def download_start(background_tasks: BackgroundTasks):
    if state.status == EGovStatus.DOWNLOADING:
        raise HTTPException(status_code=409, detail="ダウンロードが既に実行中です")

    # API トークンが有効なら API 方式で実行
    if state.is_token_valid() or state.refresh_token:
        background_tasks.add_task(_api_download)
        return {"message": "ダウンロードを開始しました (API 方式)"}

    # 保存済みトークンを確認
    saved = egov_api.load_tokens()
    if saved.get("access_token"):
        state.set_tokens(saved)
        if state.is_token_valid() or saved.get("refresh_token"):
            state.refresh_token = saved.get("refresh_token")
            background_tasks.add_task(_api_download)
            return {"message": "ダウンロードを開始しました (API 方式・保存トークン)"}

    # Playwright セッションがあるなら旧方式
    if state.status == EGovStatus.LOGGED_IN and state._page is not None:
        background_tasks.add_task(run_download)
        return {"message": "ダウンロードを開始しました (Playwright 方式)"}

    raise HTTPException(
        status_code=401,
        detail="認証が必要です。/egov/auth-url でGビズID認証を行ってください。"
    )


async def _api_download():
    """e-Gov API v2 を使って電子送達ファイルをダウンロード"""
    state.status  = EGovStatus.DOWNLOADING
    state.message = "e-Gov API で電子送達を確認中..."
    try:
        cfg = egov_api.load_api_config()
        if not cfg["client_id"] or not cfg["client_secret"]:
            raise RuntimeError("e-Gov API の資格情報が設定されていません。/egov/config で設定してください。")

        # トークン更新（期限切れの場合）
        if not state.is_token_valid() and state.refresh_token:
            state.message = "アクセストークンを更新中..."
            token_resp = await egov_api.refresh_tokens_request(
                state.refresh_token,
                cfg["client_id"],
                cfg["client_secret"],
            )
            state.set_tokens(token_resp)
            egov_api.save_tokens({
                "access_token":  state.access_token,
                "refresh_token": state.refresh_token,
                "expires_at":    state.token_expires_at,
            })

        # 電子送達一覧を取得（直近 90 日間）
        date_to   = datetime.now().strftime("%Y-%m-%d")
        date_from = (datetime.now() - timedelta(days=90)).strftime("%Y-%m-%d")
        state.message = f"電子送達一覧を取得中 ({date_from} ～ {date_to})..."

        list_resp = await egov_api.get_post_list(state.access_token, date_from, date_to)
        # OpenAPI仕様: レスポンスは results.post_list に配列
        results = list_resp.get("results", {})
        post_list = results.get("post_list", [])

        # post_list は電子送達ヘッダー一覧。各要素に notice_data_list があり
        # そこに個々の post_id が含まれる
        # フラットなリストに変換
        posts = []
        for item in post_list:
            for nd in item.get("notice_data_list", []):
                posts.append({
                    "post_id":            nd.get("post_id", ""),
                    "notice_data_name":   nd.get("notice_data_name", ""),
                    "notice_issue_date":  nd.get("notice_issue_date", ""),
                    "download_expired_date": nd.get("download_expired_date", ""),
                    "download_date":      nd.get("download_date"),
                    "title":              item.get("title", ""),
                    "issuer_organization": item.get("issuer_organization", ""),
                })

        if not posts:
            state.status  = EGovStatus.LOGGED_IN
            state.message = f"電子送達はありません ({date_from} ～ {date_to})"
            return

        # 未取得のもの（download_date が空）を優先
        pending = [p for p in posts if not p.get("download_date")]
        targets = pending if pending else posts

        egov_data = Path("/app/egov_data")
        egov_data.mkdir(parents=True, exist_ok=True)

        downloaded = []
        for post in targets:
            post_id = post.get("post_id", "")
            if not post_id:
                continue

            state.message = f"通知ファイルを取得中 (post_id={post_id})..."
            file_resp = await egov_api.get_post_file(state.access_token, post_id)

            # OpenAPI仕様: レスポンスは results.file_data / results.notice_data_name
            file_results   = file_resp.get("results", {})
            file_data_b64  = file_results.get("file_data", "")
            file_name_list = file_results.get("file_name_list", [])
            first_name     = file_name_list[0].get("file_name") if file_name_list else None
            filename = (file_results.get("notice_data_name")
                        or first_name
                        or f"egov_{post_id}.zip")

            if not file_data_b64:
                continue

            import base64 as b64mod
            file_bytes = b64mod.b64decode(file_data_b64)
            save_path  = egov_data / filename
            save_path.write_bytes(file_bytes)

            # 取得完了登録
            try:
                await egov_api.mark_post_done(state.access_token, post_id)
            except Exception:
                pass

            downloaded.append({"filename": filename, "path": str(save_path),
                                "size": len(file_bytes), "post_id": post_id})
            break  # 1件ずつ

        if downloaded:
            state.last_download = downloaded[0]
            state.status  = EGovStatus.LOGGED_IN
            state.message = (f"ダウンロード完了: {downloaded[0]['filename']} "
                             f"({downloaded[0]['size']:,} bytes) "
                             f"/ 全 {len(posts)} 件")
        else:
            state.status  = EGovStatus.LOGGED_IN
            state.message = "ダウンロード可能なファイルがありませんでした"

    except Exception as e:
        state.status  = EGovStatus.ERROR
        state.message = f"API ダウンロードエラー: {e}"


# ── OAuth2 / e-Gov API エンドポイント ──────────────────────────

@router.post("/config")
async def save_config(req: ConfigRequest):
    """e-Gov API 資格情報を保存"""
    egov_api.save_api_config(req.software_id, req.api_key, req.redirect_uri)
    return {"message": "設定を保存しました"}


@router.get("/config-info")
async def get_config_info():
    """現在の設定情報（APIキーは非表示）を返す"""
    import os
    cfg = egov_api.load_api_config()
    return {
        "software_id":   cfg.get("client_id", ""),
        "redirect_uri":  cfg.get("redirect_uri", ""),
        "environment":   os.environ.get("EGOV_ENV", "production"),
    }


@router.get("/auth-url")
async def get_auth_url():
    """OAuth2 + PKCE 認可 URL を生成して返す"""
    cfg = egov_api.load_api_config()
    if not cfg["client_id"] or not cfg["client_secret"]:
        raise HTTPException(
            status_code=400,
            detail="e-Gov API の資格情報が未設定です。先に /egov/config を呼び出してください。"
        )

    verifier, challenge = egov_api.generate_pkce()
    oauth_state = secrets.token_urlsafe(16)

    state._pkce_verifier = verifier
    state._oauth_state   = oauth_state

    auth_url = egov_api.build_auth_url(
        cfg["client_id"],
        cfg["redirect_uri"],
        challenge,
        oauth_state,
    )
    return {"auth_url": auth_url, "state": oauth_state}


@router.post("/callback")
async def oauth_callback(req: CallbackRequest):
    """
    e-Gov 認可コードを受け取りアクセストークンへ交換する。
    egov.html の JavaScript から呼び出される。
    """
    if not state._pkce_verifier:
        raise HTTPException(status_code=400, detail="PKCE セッションが見つかりません。再度 /egov/auth-url から開始してください。")

    if req.state != state._oauth_state:
        raise HTTPException(status_code=400, detail="state パラメータが一致しません（CSRF防止）")

    cfg = egov_api.load_api_config()
    try:
        token_resp = await egov_api.exchange_code(
            req.code,
            cfg["client_id"],
            cfg["client_secret"],
            cfg["redirect_uri"],
            state._pkce_verifier,
        )
    except Exception as e:
        raise HTTPException(status_code=502, detail=f"トークン取得失敗: {e}")

    state.set_tokens(token_resp)
    state._pkce_verifier = None
    state._oauth_state   = None
    state.status  = EGovStatus.LOGGED_IN
    state.message = "e-Gov API 認証完了"

    # ファイルに永続化
    egov_api.save_tokens({
        "access_token":  state.access_token,
        "refresh_token": state.refresh_token,
        "expires_at":    state.token_expires_at,
    })

    return {"message": "認証完了しました", "status": "logged_in"}


@router.get("/downloads/latest")
async def download_latest():
    """最後にダウンロードしたファイルを返す"""
    if not state.last_download:
        raise HTTPException(status_code=404, detail="ダウンロード済みファイルがありません")
    from pathlib import Path
    path = Path(state.last_download["path"])
    if not path.exists():
        raise HTTPException(status_code=404, detail="ファイルが見つかりません")
    return FileResponse(
        path=str(path),
        filename=state.last_download["filename"],
        media_type="application/octet-stream",
    )


@router.get("/debug-page")
async def debug_page():
    """現在のブラウザページの詳細診断情報（デバッグ用）"""
    if state._page is None:
        return {"error": "ブラウザセッションなし", "status": state.status}
    try:
        page = state._page
        diag = await page.evaluate("""
            () => {
                const b = document.body ? document.body.innerText : '(empty)';
                return {
                    url:         location.href,
                    title:       document.title,
                    hasLogout:   b.includes('ログアウト'),
                    hasMyPage:   b.includes('マイページ'),
                    hasDelivery: b.includes('電子送達'),
                    bodyLen:     b.length,
                    bodyPreview: b.slice(0, 600).replace(/\\n+/g, ' | '),
                    links: Array.from(document.querySelectorAll('a')).map(a => ({
                        text: a.textContent.trim().slice(0, 50),
                        href: a.href,
                        imgAlts: Array.from(a.querySelectorAll('img')).map(img => img.alt).join(','),
                    })).slice(0, 30),
                    buttons: Array.from(document.querySelectorAll('button, input[type="submit"], input[type="button"], [role="button"]')).map(el => ({
                        tag:  el.tagName,
                        text: (el.textContent || el.value || '').trim().slice(0, 50),
                        id:   el.id,
                        cls:  el.className.slice(0, 60),
                    })).slice(0, 20),
                    imgs: Array.from(document.querySelectorAll('img[alt]')).map(img => ({
                        alt: img.alt,
                        src: img.src.slice(0, 80),
                    })).slice(0, 20),
                    inputs: Array.from(document.querySelectorAll('input, select')).map(el => ({
                        type: el.type,
                        name: el.name,
                        id:   el.id,
                    })).slice(0, 20),
                };
            }
        """)
        return {"browser_state": state.status, **diag}
    except Exception as e:
        return {"error": str(e), "status": state.status}


# ── 申請書作成・一覧 エンドポイント ────────────────────────────

async def _ensure_valid_token() -> str:
    """有効なアクセストークンを返す（期限切れ時は自動更新）"""
    cfg = egov_api.load_api_config()
    if not cfg["client_id"] or not cfg["client_secret"]:
        raise HTTPException(status_code=400, detail="e-Gov API 資格情報が未設定です")

    # メモリが空の場合はファイルから expires_at を正しく復元
    if not state.access_token:
        saved = egov_api.load_tokens()
        if saved.get("access_token"):
            state.access_token     = saved["access_token"]
            state.refresh_token    = saved.get("refresh_token")
            state.token_expires_at = saved.get("expires_at")  # float タイムスタンプをそのまま復元

    # トークンが期限切れ → リフレッシュを試みる
    if not state.is_token_valid() and state.refresh_token:
        try:
            token_resp = await egov_api.refresh_tokens_request(
                state.refresh_token, cfg["client_id"], cfg["client_secret"]
            )
            state.set_tokens(token_resp)
            egov_api.save_tokens({
                "access_token":  state.access_token,
                "refresh_token": state.refresh_token,
                "expires_at":    state.token_expires_at,
            })
        except Exception as e:
            state.access_token = None
            raise HTTPException(
                status_code=401,
                detail=f"トークン更新失敗: {e}。再度 GビズIDログイン が必要です。"
            )

    if not state.access_token:
        raise HTTPException(status_code=401, detail="認証が必要です。GビズID でログインしてください。")

    if not state.is_token_valid():
        raise HTTPException(
            status_code=401,
            detail="アクセストークンが期限切れです。再度 GビズIDログイン してください。"
        )

    return state.access_token


@router.post("/test-apply")
async def test_apply(req: ApplyRequest):
    """
    テスト申請書作成：スケルトン（ひな形）を取得してそのまま申請送信する。
    到達番号（arrive_id）を返す。
    """
    import re
    if not re.fullmatch(r"[A-Za-z0-9]{16}", req.proc_id):
        raise HTTPException(status_code=400, detail="proc_id は半角英数字16文字です")

    access_token = await _ensure_valid_token()

    # 1. スケルトン取得
    try:
        skel_resp = await egov_api.get_procedure_skeleton(access_token, req.proc_id)
    except Exception as e:
        raise HTTPException(status_code=502, detail=f"スケルトン取得失敗: {e}")

    results = skel_resp.get("results", {})
    file_data_b64 = results.get("file_data", "")
    if not file_data_b64:
        raise HTTPException(status_code=502, detail="スケルトンデータが空です")

    # 2. 申請送信（スケルトンをそのまま送信）
    #    電子申請手続 → POST /apply（トライアルモード）
    #    電子送達手続 → POST /post-apply（トライアル非対応のため自動フォールバック）
    #    電子送達手続の場合は kousei.xml に必須フィールドを埋めてから送信する
    file_name = f"{req.proc_id}.zip"
    apply_resp = None
    api_type = "電子申請"
    try:
        apply_resp = await egov_api.submit_apply(
            access_token, req.proc_id, file_name, file_data_b64, trial=True
        )
    except RuntimeError as e:
        err_str = str(e)
        if "対象手続ではありません" in err_str or "post-apply" in err_str.lower():
            # 電子送達利用申込み手続 → kousei.xml 修正 → /post-apply
            api_type = "電子送達利用申込み"
            # 公式手続一覧 egov_applapi_testproclist.xlsx より正しい手続名称
            proc_name = "ＡＰＩテスト用手続（電子送達関係手続）（通）０００１／ＡＰＩテスト用手続（電子送達関係手続）（通）０００１"
            # スケルトン kousei.xml に必須フィールドを埋める
            try:
                modified_file_data = egov_api.modify_skeleton_for_post_apply(
                    file_data_b64=file_data_b64,
                    proc_id=req.proc_id,
                    proc_name=proc_name,
                    uketsuke_kikan_id="100900",  # 電子送達関係手続の受付行政機関ID
                    shinsei_shubetsu="新規申請",
                    shimei="テスト　タロウ",
                    shimei_furigana="テスト　タロウ",
                    yubin="1300022",
                    jusho="東京都墨田区江東橋４丁目３１番１０号",
                )
            except Exception as emx:
                raise HTTPException(status_code=502, detail=f"スケルトン修正失敗: {emx}")
            try:
                apply_resp = await egov_api.submit_post_apply(
                    access_token, req.proc_id, file_name, modified_file_data
                )
            except Exception as e2:
                raise HTTPException(status_code=502, detail=f"電子送達利用申込み失敗: {e2}")
        else:
            raise HTTPException(status_code=502, detail=f"申請送信失敗: {err_str}")
    except Exception as e:
        raise HTTPException(status_code=502, detail=f"申請送信失敗: {e}")

    res = apply_resp.get("results", {})
    return {
        "arrive_id":   res.get("arrive_id", ""),
        "arrive_date": res.get("arrive_date", ""),
        "proc_name":   res.get("proc_name", ""),
        "proc_id":     req.proc_id,
        "api_type":    api_type,
        "raw":         res,
    }


@router.get("/apply-list")
async def apply_list():
    """申請案件一覧取得"""
    access_token = await _ensure_valid_token()

    try:
        resp = await egov_api.get_apply_list(access_token)
    except Exception as e:
        raise HTTPException(status_code=502, detail=f"申請案件一覧取得失敗: {e}")

    results = resp.get("results", {})
    apply_list_data = results.get("apply_list", [])

    items = []
    for item in apply_list_data:
        items.append({
            "arrive_id":    item.get("arrive_id", ""),
            "arrive_date":  item.get("arrive_date", ""),
            "proc_id":      item.get("proc_id", ""),
            "proc_name":    item.get("proc_name", ""),
            "status":       item.get("status", ""),
            "sub_status":   item.get("sub_status", ""),
        })
    return {"apply_list": items, "total": len(items)}
