#!/usr/bin/env python3
"""
Daily Orchestrator — walmart.uscareers.co.com
========================================
Intelligent scheduling:
  - Days 1-4:  Build phase (heavy master.py runs, one-time)
  - Days 5-17: Maintenance cycle (lighter, repeats forever after day 17)
  - api.py only runs on days master.py runs

Schedule:
  Day 1:  master(n, fo, n, fo, n)     — 5 runs
  Day 2:  master(n, n, n, n)           — 4 runs
  Day 3:  master(n, fo, n, fo)         — 4 runs
  Day 4:  master(n, n, n)              — 3 runs
  Day 5:  master(n, fo)                — 2 runs
  Day 6:  master(n)                    — 1 run
  Day 7:  skip
  Day 8:  skip
  Day 9:  skip
  Day 10: master(n)                    — 1 run
  Day 11: skip
  Day 12: master(n)                    — 1 run
  Day 13: skip
  Day 14: master(fo, n)                — 2 runs
  Day 15: master(n)                    — 1 run
  Day 16: skip
  Day 17: skip
  Day 18: master(n)                    — 1 run
  → loops back to Day 5 pattern

  n = normal, fo = --force-old
  api.py runs ONLY on days master.py runs

Usage:
    python3.9 daily.py --creds gsc-bot.json

Requirements:
    pip install google-api-python-client google-auth google-auth-httplib2 requests
"""

import argparse
import json
import logging
import os
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path


# ╔═════════════════════════════════════════════════════════════╗
# ║  CONFIG                                                    ║
# ╚═════════════════════════════════════════════════════════════╝
PYTHON = "python3.9"
HTACCESS = ".htaccess"
HTACCESS_TEMP = "1htaccess"
JOBS_DIR = "jobs"
LOG_DIR = "daily_logs"

UPDATER_SCRIPT = "updater-5.py"
MASTER_SCRIPT = "master.py"
BRAIN_SCRIPT = "brain.py"
API_SCRIPT = "api.py"

STATE_FILE = ".daily_state.json"
# ─────────────────────────────────────────────────────────────

# n = normal run, fo = --force-old
N = "normal"
FO = "force-old"

# Build phase: days 1-4 (runs once, never repeats)
BUILD_SCHEDULE = {
    1: [N, FO, N, FO, N],
    2: [N, N, N, N],
    3: [N, FO, N, FO],
    4: [N, N, N],
}

# Maintenance cycle: days 5-18, then loops back to day 5
CYCLE_SCHEDULE = {
    5:  [N, FO],
    6:  [N],
    7:  [],          # skip
    8:  [],          # skip
    9:  [],          # skip
    10: [N],
    11: [],          # skip
    12: [N],
    13: [],          # skip
    14: [FO, N],
    15: [N],
    16: [],          # skip
    17: [],          # skip
    18: [N],
}

CYCLE_START = 5
CYCLE_END = 18
CYCLE_LENGTH = CYCLE_END - CYCLE_START + 1  # 14 days


# ---------------------------------------------------------------------------
# Logging
# ---------------------------------------------------------------------------
def setup_logging() -> Path:
    Path(LOG_DIR).mkdir(exist_ok=True)
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    log_file = Path(LOG_DIR) / f"daily_{timestamp}.log"

    logging.basicConfig(
        level=logging.INFO,
        format="%(asctime)s  %(message)s",
        datefmt="%H:%M:%S",
        handlers=[
            logging.FileHandler(log_file, encoding="utf-8"),
            logging.StreamHandler(sys.stdout),
        ],
    )
    return log_file


# ---------------------------------------------------------------------------
# State management (tracks which day we're on)
# ---------------------------------------------------------------------------
def load_state() -> dict:
    path = Path(STATE_FILE)
    if path.exists():
        try:
            return json.loads(path.read_text())
        except (json.JSONDecodeError, TypeError):
            pass
    return {"day": 0, "last_run": None}


def save_state(state: dict):
    Path(STATE_FILE).write_text(json.dumps(state, indent=2))


def get_current_day(state: dict) -> int:
    """
    Advance to next day. Days 1-4 are build phase (one-time).
    After day 4, enter the cycle: days 5-18 repeating.
    """
    last_day = state.get("day", 0)

    if last_day < 4:
        # Still in build phase
        return last_day + 1
    else:
        # In maintenance cycle
        if last_day < CYCLE_START:
            return CYCLE_START
        elif last_day >= CYCLE_END:
            return CYCLE_START  # loop back
        else:
            return last_day + 1


def get_master_schedule(day: int) -> list[str]:
    """Get the master.py run schedule for a given day."""
    if day in BUILD_SCHEDULE:
        return BUILD_SCHEDULE[day]
    elif day in CYCLE_SCHEDULE:
        return CYCLE_SCHEDULE[day]
    else:
        return []


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def run_command(cmd: list[str], description: str) -> bool:
    logging.info(f"[*] {description}")
    logging.info(f"    $ {' '.join(cmd)}")

    start = time.time()
    try:
        result = subprocess.run(cmd, text=True)
        elapsed = time.time() - start

        if result.returncode == 0:
            logging.info(f"    [+] Done ({elapsed:.1f}s)")
            return True
        else:
            logging.error(f"    [!] Exit code {result.returncode} ({elapsed:.1f}s)")
            return False

    except FileNotFoundError:
        logging.error(f"    [!] Command not found: {cmd[0]}")
        return False
    except Exception as e:
        logging.error(f"    [!] Error: {e}")
        return False


# ---------------------------------------------------------------------------
# Step 1: Handle .htaccess
# ---------------------------------------------------------------------------
def rename_htaccess_away() -> bool:
    if Path(HTACCESS).exists():
        try:
            os.rename(HTACCESS, HTACCESS_TEMP)
            logging.info(f"[+] Renamed {HTACCESS} → {HTACCESS_TEMP}")
            return True
        except OSError as e:
            logging.error(f"[!] Could not rename {HTACCESS}: {e}")
            return False
    else:
        logging.info(f"[=] No {HTACCESS} found — skipping")
        return False


def restore_htaccess():
    if Path(HTACCESS_TEMP).exists():
        try:
            os.rename(HTACCESS_TEMP, HTACCESS)
            logging.info(f"[+] Restored {HTACCESS_TEMP} → {HTACCESS}")
        except OSError as e:
            logging.error(f"[!] Could not restore: {e}")
    else:
        logging.info(f"[=] No {HTACCESS_TEMP} to restore")


# ---------------------------------------------------------------------------
# Step 2: Updater
# ---------------------------------------------------------------------------
def run_updater():
    if Path(JOBS_DIR).is_dir():
        logging.info(f"[+] jobs/ folder exists — running updater")
        run_command([PYTHON, UPDATER_SCRIPT], "updater5.py")
    else:
        logging.info(f"[=] No jobs/ folder — skipping updater")


# ---------------------------------------------------------------------------
# Step 3: Master (intelligent schedule)
# ---------------------------------------------------------------------------
def run_master_sequence(schedule: list[str]):
    total = len(schedule)
    for i, mode in enumerate(schedule, 1):
        if mode == FO:
            cmd = [PYTHON, MASTER_SCRIPT, "--force-old"]
            desc = f"master.py --force-old ({i}/{total})"
        else:
            cmd = [PYTHON, MASTER_SCRIPT]
            desc = f"master.py ({i}/{total})"

        ok = run_command(cmd, desc)
        if not ok:
            logging.warning(f"[!] {desc} failed — continuing")
        time.sleep(1)


# ---------------------------------------------------------------------------
# Step 4: Brain (GSC sitemaps + inspection)
# ---------------------------------------------------------------------------
def run_brain():
    run_command(
        [PYTHON, BRAIN_SCRIPT],
        "brain.py — GSC sitemaps + inspection"
    )


# ---------------------------------------------------------------------------
# Step 5: API (push job posts) — only if master ran
# ---------------------------------------------------------------------------
def run_api():
    run_command([PYTHON, API_SCRIPT], "api.py — Indexing API")


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
    parser = argparse.ArgumentParser(description="Daily Orchestrator — walmart.uscareers.co.com")
    parser.add_argument("--skip-brain", action="store_true", help="Skip brain.py")
    parser.add_argument("--reset-day", type=int, help="Force reset to specific day number")
    args = parser.parse_args()

    log_file = setup_logging()

    # Load state and figure out which day we're on
    state = load_state()

    if args.reset_day is not None:
        state["day"] = args.reset_day - 1  # -1 because get_current_day adds 1
        save_state(state)
        logging.info(f"[*] Reset to day {args.reset_day}")

    current_day = get_current_day(state)
    schedule = get_master_schedule(current_day)
    master_runs = len(schedule)
    master_will_run = master_runs > 0

    logging.info("=" * 60)
    logging.info("  Daily Orchestrator — walmart.uscareers.co.com")
    logging.info(f"  {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    logging.info(f"  Day: {current_day}")
    if master_will_run:
        schedule_display = ", ".join("--force-old" if s == FO else "normal" for s in schedule)
        logging.info(f"  Master: {master_runs} run(s) [{schedule_display}]")
        logging.info(f"  API: will run (master is running)")
    else:
        logging.info(f"  Master: SKIP today")
        logging.info(f"  API: SKIP (no master = no api)")
    logging.info("=" * 60)

    # ── Step 1: Rename .htaccess ──
    logging.info("\n── Step 1: Handle .htaccess ──")
    htaccess_renamed = rename_htaccess_away()

    try:
        # ── Step 2: Updater ──
        logging.info("\n── Step 2: Updater ──")
        run_updater()

        # ── Step 3: Master (schedule-based) ──
        if master_will_run:
            logging.info(f"\n── Step 3: Master x{master_runs} ──")
            run_master_sequence(schedule)
        else:
            logging.info("\n── Step 3: Master (SKIP — Day {}) ──".format(current_day))

        # ── Step 4: Brain (GSC) — always runs ──
        if not args.skip_brain:
            logging.info("\n── Step 4: Brain (GSC) ──")
            run_brain()
        else:
            logging.info("\n── Step 4: Brain (SKIPPED) ──")

        # ── Step 5: API — only if master ran ──
        if master_will_run:
            logging.info("\n── Step 5: API (Job Posts) ──")
            run_api()
        else:
            logging.info("\n── Step 5: API (SKIP — no master today) ──")

    finally:
        # ── Step 6: Restore .htaccess (ALWAYS) ──
        logging.info("\n── Step 6: Restore .htaccess ──")
        if htaccess_renamed:
            restore_htaccess()
        else:
            logging.info("[=] Nothing to restore")

    # Save state for next run
    state["day"] = current_day
    state["last_run"] = datetime.now().isoformat()
    save_state(state)

    logging.info("\n" + "=" * 60)
    logging.info(f"  Day {current_day} complete!")

    # Preview next day
    next_day = get_current_day(state)
    next_schedule = get_master_schedule(next_day)
    if next_schedule:
        next_display = ", ".join("--force-old" if s == FO else "normal" for s in next_schedule)
        logging.info(f"  Tomorrow (Day {next_day}): master x{len(next_schedule)} [{next_display}] + api")
    else:
        logging.info(f"  Tomorrow (Day {next_day}): brain only (no master, no api)")

    logging.info(f"  Log: {log_file}")
    logging.info("=" * 60)


if __name__ == "__main__":
    main()
