"""
ガールズヘブン アクセス統計取得スクリプト

GH管理画面（manager.girlsheaven-job.net）の「求人ログ」ページから
Highcharts のアクセスデータを取得し、gh_access_stats テーブルに保存する。

データは30分刻み48件（00:00〜23:30）。
  インデックス 0 → 00:00、1 → 00:30、...47 → 23:30

Usage:
    python tools/get_access_girlsheaven.py <client_id>

タスクスケジューラ登録:
    タスク名   : RankRadar_GetAccess_GH_Client1
    実行時刻   : 毎日 05:45
    実行コマンド: C:\\apps\\ranking_system\\venv\\Scripts\\python.exe
                  C:\\apps\\ranking_system\\python\\tools\\get_access_girlsheaven.py 1
"""

import argparse
import logging
import os
import random
import re
import sys
from datetime import date, datetime
from pathlib import Path

from playwright.sync_api import Page, sync_playwright
from playwright_stealth import Stealth

sys.path.append(str(Path(__file__).resolve().parents[1]))
from config.database import execute_query, execute_update

# ---------------------------------------------------------------------------
# 定数
# ---------------------------------------------------------------------------
LOGIN_URL  = "https://manager.girlsheaven-job.net/"
JOBLOG_URL = "https://manager.girlsheaven-job.net/joblog/"
MEDIA_NAME = "girlsheaven"
PAGE_TIMEOUT = 30_000  # ms

VIEWPORTS = [
    {"width": 1280, "height": 800},
    {"width": 1366, "height": 768},
    {"width": 1920, "height": 1080},
    {"width": 1440, "height": 900},
]

# ---------------------------------------------------------------------------
# ロガー設定
# ---------------------------------------------------------------------------
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
)
logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# DB ヘルパー
# ---------------------------------------------------------------------------
def get_tool_settings(client_id: int) -> dict | None:
    """カゴヤDB の media_settings から GH ログイン情報を取得する。"""
    rows = execute_query(
        "SELECT login_id, login_pass FROM media_settings "
        "WHERE client_id = %s AND media_name = 'girlsheaven' AND is_active = 1 LIMIT 1",
        (client_id,),
        local=False,
    )
    if not rows:
        return None
    return {"login_id": rows[0]["login_id"], "login_pass": rows[0]["login_pass"]}


def save_access_stats(client_id: int, stat_date: date, records: list[dict]) -> None:
    """gh_access_stats テーブルに UPSERT する。

    事前に以下のALTER TABLEをphpMyAdmin等で実行しておく必要がある（rankradar_data）:
        ALTER TABLE gh_access_stats ADD COLUMN shop_access INT DEFAULT 0;
    """
    sql = """
        INSERT INTO gh_access_stats
            (client_id, stat_date, hour_slot, total_access, shop_access, fetched_at)
        VALUES (%s, %s, %s, %s, %s, NOW())
        ON DUPLICATE KEY UPDATE
            total_access = VALUES(total_access),
            shop_access  = VALUES(shop_access),
            fetched_at   = NOW()
    """
    for rec in records:
        execute_update(
            sql,
            (client_id, stat_date, rec["hour_slot"], rec["total_access"], rec["shop_access"]),
            local=True,
        )
    logger.info("gh_access_stats 保存完了: %d件 (stat_date=%s)", len(records), stat_date)


def save_error_log(client_id: int, message: str) -> None:
    """error_logs にエラーを記録する（local=True）。"""
    try:
        execute_update(
            """
            INSERT INTO error_logs
                (client_id, media_name, error_type, error_message, occurred_at)
            VALUES (%s, %s, %s, %s, %s)
            """,
            (client_id, MEDIA_NAME, "gh_access_error", message, datetime.now()),
            local=True,
        )
    except Exception as e:
        logger.error("エラーログのDB保存も失敗: %s", e)


# ---------------------------------------------------------------------------
# ブラウザ起動
# ---------------------------------------------------------------------------
def build_browser(p):
    """HEADLESS 環境変数に従いブラウザを起動し、(browser, page) を返す。"""
    headless = os.getenv("HEADLESS", "true").lower() != "false"
    if headless:
        browser = p.chromium.launch(
            headless=True,
            args=["--no-sandbox", "--disable-blink-features=AutomationControlled"],
        )
    else:
        browser = p.chromium.launch(
            headless=False,
            executable_path=r"C:\Program Files\Google\Chrome\Application\chrome.exe",
            slow_mo=500,
            args=["--no-sandbox", "--disable-blink-features=AutomationControlled"],
        )
    context = browser.new_context(
        user_agent=(
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/124.0.0.0 Safari/537.36"
        ),
        viewport=random.choice(VIEWPORTS),
        locale="ja-JP",
        timezone_id="Asia/Tokyo",
        extra_http_headers={
            "Accept": (
                "text/html,application/xhtml+xml,"
                "application/xml;q=0.9,"
                "image/avif,image/webp,*/*;q=0.8"
            ),
            "Accept-Language": "ja,en-US;q=0.7,en;q=0.3",
            "Accept-Encoding": "gzip, deflate, br",
            "Connection": "keep-alive",
            "Upgrade-Insecure-Requests": "1",
            "Sec-Fetch-Dest": "document",
            "Sec-Fetch-Mode": "navigate",
            "Sec-Fetch-Site": "none",
            "Sec-Fetch-User": "?1",
        },
    )
    page = context.new_page()
    Stealth().apply_stealth_sync(page)
    return browser, page


# ---------------------------------------------------------------------------
# アクセスデータ取得本体
# ---------------------------------------------------------------------------
def get_access_stats(page: Page, login_id: str, login_pass: str) -> list[dict]:
    """GH管理画面にログインし、joblog ページの Highcharts データを取得して返す。

    Returns:
        list of dict with keys: hour_slot (str "HH:MM"), total_access (int), shop_access (int)
    """
    # ① ログインページへ
    logger.info("GH管理画面にアクセス: %s", LOGIN_URL)
    page.goto(LOGIN_URL, timeout=PAGE_TIMEOUT, wait_until="domcontentloaded")
    page.wait_for_timeout(random.randint(2000, 3000))

    # ② ログインフォームに入力してJavaScriptで直接submit
    logger.info("ログインフォームに入力します")
    page.fill("#loginId", login_id)
    page.wait_for_timeout(random.randint(300, 700))
    page.fill("#loginPass", login_pass)
    page.wait_for_timeout(random.randint(300, 700))

    logger.info("JavaScriptでフォームをsubmitします")
    page.evaluate("document.getElementById('login_form').submit()")

    # submit後はページ遷移を待つ
    try:
        page.wait_for_load_state("domcontentloaded", timeout=15000)
    except Exception:
        pass
    page.wait_for_timeout(2000)

    current_url = page.url
    logger.info("フォームsubmit後 | 現在URL: %s", current_url)

    # ログイン失敗チェック（さらに待機して再確認）
    if "/top/" not in current_url:
        page.wait_for_timeout(3000)
        current_url = page.url
        if "/top/" not in current_url:
            login_html = page.content()
            logger.info("=== ログイン後HTML (先頭2000文字) ===\n%s", login_html[:2000])
            raise RuntimeError(f"ログイン失敗: URLが /top/ に遷移しませんでした ({current_url})")

    logger.info("ログイン成功 | URL: %s", current_url)

    # ③ joblog ページへ移動（networkidleで完全ロードを待つ）
    logger.info("求人ログページへ移動: %s", JOBLOG_URL)
    page.goto(JOBLOG_URL, timeout=PAGE_TIMEOUT, wait_until="domcontentloaded")
    try:
        page.wait_for_load_state("networkidle", timeout=30000)
    except Exception:
        pass
    page.wait_for_timeout(5000)

    # デバッグ: ページHTMLを出力して構造を確認
    html_content = page.content()
    logger.info("=== PAGE HTML (先頭3000文字) ===\n%s", html_content[:3000])
    logger.info("=== PAGE URL: %s ===", page.url)

    # HTMLをファイルに保存
    try:
        with open(r'C:\apps\ranking_system\joblog_debug.html', 'w', encoding='utf-8') as f:
            f.write(html_content)
        logger.info("HTMLをC:\\apps\\ranking_system\\joblog_debug.htmlに保存しました")
    except Exception as e:
        logger.warning("HTMLファイル保存失敗: %s", e)

    # ④ HTMLから正規表現でデータを抽出（{ y: 数値 , marker: { enabled: false } }）
    logger.info("HTMLから正規表現でデータを抽出します")
    pattern = r'\{\s*y:\s*(\d+)\s*,\s*marker:\s*\{\s*enabled:\s*false\s*\}\s*\}'
    matches = re.findall(pattern, html_content)
    logger.info("正規表現マッチ数: %d件", len(matches))

    if len(matches) < 1:
        logger.error("抽出件数が0件: データなし")
        raise RuntimeError("データ抽出失敗: 0件")

    all_values = [int(v) for v in matches]
    logger.info("HTMLから%d件のデータを抽出しました（生データ全件）: %s", len(all_values), all_values)

    # ⑤ GH全体アクセス（インデックス0〜47）と自店アクセス（インデックス48〜）に分割
    total_values = all_values[:48]
    shop_values  = all_values[48:]
    logger.info("GH全体アクセス: %d件 / 自店アクセス: %d件", len(total_values), len(shop_values))

    # 自店データが48件未満の場合は0で補完
    while len(shop_values) < 48:
        shop_values.append(0)

    # ⑥ インデックス→hour_slot に変換（0→00:00、1→00:30、...47→23:30）
    records = []
    for i in range(48):
        hour      = i // 2
        minute    = 30 if i % 2 else 0
        hour_slot = f"{hour:02d}:{minute:02d}"
        records.append({
            "hour_slot":    hour_slot,
            "total_access": total_values[i],
            "shop_access":  shop_values[i],
        })
        logger.info("  [%02d] hour_slot=%s total_access=%d shop_access=%d",
                    i, hour_slot, total_values[i], shop_values[i])

    logger.info("データ解析完了: %d件", len(records))
    return records


# ---------------------------------------------------------------------------
# エントリポイント
# ---------------------------------------------------------------------------
def main() -> None:
    parser = argparse.ArgumentParser(description="ガールズヘブン アクセス統計取得")
    parser.add_argument("client_id", type=int, help="クライアントID")
    args = parser.parse_args()
    client_id = args.client_id

    logger.info("GHアクセス統計取得開始 | client_id=%d", client_id)

    # tool_settings 取得
    try:
        settings = get_tool_settings(client_id)
    except Exception as e:
        logger.error("tool_settings 取得エラー: %s", e)
        save_error_log(client_id, f"tool_settings取得失敗: {e}")
        sys.exit(1)

    if settings is None:
        logger.error("client_id=%d の tool_settings(benry) が見つかりません", client_id)
        save_error_log(client_id, f"tool_settingsが見つかりません (client_id={client_id})")
        sys.exit(1)

    # ブラウザでデータ取得
    try:
        with sync_playwright() as p:
            browser, page = build_browser(p)
            try:
                records = get_access_stats(page, settings["login_id"], settings["login_pass"])
            finally:
                browser.close()
    except Exception as e:
        error_msg = f"アクセス統計取得失敗 (client_id={client_id}): {e}"
        logger.error(error_msg)
        save_error_log(client_id, error_msg)
        sys.exit(1)

    # 前日分として stat_date を設定（05:45 実行のため前日データを対象とする）
    stat_date = date.today()

    # DB に保存
    try:
        save_access_stats(client_id, stat_date, records)
    except Exception as e:
        error_msg = f"gh_access_stats 保存失敗 (client_id={client_id}): {e}"
        logger.error(error_msg)
        save_error_log(client_id, error_msg)
        sys.exit(1)

    logger.info("完了 | client_id=%d stat_date=%s 件数=%d", client_id, stat_date, len(records))


if __name__ == "__main__":
    main()
