"""
SEAO Scraper - Main Entry Point
Runs the scraper once or on a 24-hour loop, storing results in SQLite.
"""

import asyncio
import logging
import sys
import os
import time

from playwright.async_api import async_playwright

import config
from database import Database
from scraper import refresh_existing_details, scrape_all
from mysql_sync import MySQLSync

# Add parent directory to path for imports when running standalone
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

# Configure logging
logging.basicConfig(
    level=config.LOG_LEVEL,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger("seao")


async def run_scrape_job(db: Database) -> dict:
    """
    Execute a single scrape job.

    Args:
        db: Database instance for storing results.

    Returns:
        Dictionary with job statistics.
    """
    logger.info("=" * 60)
    logger.info("Starting SEAO scrape job")
    date_from, date_to = config.get_date_range()
    logger.info(f"Date range: {date_from} → {date_to}")
    logger.info(f"Database: {config.DB_PATH}")
    logger.info("=" * 60)

    stats_before = db.get_stats()
    logger.info(
        f"Database state before: {stats_before['total_contracts']} total contracts"
    )

    run_id = db.start_run(date_from, date_to)
    try:
        async with async_playwright() as pw:
            browser = await pw.chromium.launch(
                headless=config.HEADLESS,
                args=[
                    "--disable-blink-features=AutomationControlled",
                    "--no-sandbox",
                    "--disable-setuid-sandbox",
                    "--disable-dev-shm-usage",
                ],
            )

            try:
                contracts = await scrape_all(browser)
            finally:
                await browser.close()
    except Exception as exc:
        db.finish_run(run_id, "failed", error_message=str(exc))
        raise

    # Store in database
    if contracts:
        inserted, skipped = db.insert_many(contracts)
        logger.info(f"Inserted: {inserted} new | Skipped: {skipped} duplicates")

        # Store submissions for each contract
        subs_inserted = 0
        subs_deleted = 0
        contracts_with_subs = 0
        for contract in contracts:
            submissions = contract.get("submissions", [])
            if submissions:
                ins, dlt = db.upsert_submissions(contract["item_id"], submissions)
                subs_inserted += ins
                subs_deleted += dlt
                contracts_with_subs += 1
        if contracts_with_subs:
            logger.info(
                f"Submissions: {subs_inserted} inserted, {subs_deleted} deleted "
                f"across {contracts_with_subs} contracts"
            )
    else:
        inserted, skipped = 0, 0
        logger.warning("No contracts scraped!")

    if config.REFRESH_ALL_DETAILS_BEFORE_SYNC:
        logger.info("Refreshing existing SQLite contract details before the full sync...")
        existing_contracts = db.get_all_contracts_with_submissions()
        async with async_playwright() as pw:
            browser = await pw.chromium.launch(
                headless=config.HEADLESS,
                args=["--disable-blink-features=AutomationControlled", "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"],
            )
            try:
                refreshed_contracts = await refresh_existing_details(browser, existing_contracts)
            finally:
                await browser.close()

        db.insert_many(refreshed_contracts)
        for contract in refreshed_contracts:
            if contract.get("submissions"):
                db.upsert_submissions(contract["item_id"], contract["submissions"])

    stats_after = db.get_stats()
    logger.info(
        f"Database state after: {stats_after['total_contracts']} total contracts"
    )

    job_result = {
        "scraped": len(contracts),
        "inserted": inserted,
        "skipped": skipped,
        "total_in_db": stats_after["total_contracts"],
    }

    # Sync to remote MySQL if configured
    if config.SYNC_AFTER_SCRAPER and config.MYSQL_API_URL and config.MYSQL_API_TOKEN:
        logger.info("=" * 60)
        logger.info("Starting sync to remote MySQL via Laravel API...")
        logger.info(f"API URL: {config.MYSQL_API_URL}")
        logger.info("=" * 60)
        try:
            sync_contracts = db.get_all_contracts_with_submissions() if config.SYNC_ALL_FROM_SQLITE else contracts
            logger.info(f"Syncing {len(sync_contracts)} contracts ({'full SQLite dataset' if config.SYNC_ALL_FROM_SQLITE else 'current scrape'})")
            sync = MySQLSync(config.MYSQL_API_URL, config.MYSQL_API_TOKEN)
            sync_result = sync.push_contracts(sync_contracts)
            job_result["sync"] = sync_result
            logger.info(f"Sync result: {sync_result}")
        except Exception as exc:
            logger.error(f"Sync failed: {exc}", exc_info=True)
            job_result["sync_error"] = str(exc)
    else:
        logger.info("Sync to remote MySQL disabled or not configured")
        job_result["sync"] = "disabled"

    db.finish_run(run_id, "completed", scraped=len(contracts), inserted=inserted,
                  skipped=skipped, sync_failed=job_result.get("sync", {}).get("failed", 0)
                  if isinstance(job_result.get("sync"), dict) else 0)
    logger.info(f"Job complete: {job_result}")
    return job_result


async def run_once():
    """Run a single scrape and exit."""
    db = Database(config.DB_PATH)
    try:
        await run_scrape_job(db)
    except Exception as exc:
        logger.error(f"Scrape job failed: {exc}", exc_info=True)
        sys.exit(1)


async def run_loop():
    """Run the scraper in an infinite loop with 24-hour sleep."""
    db = Database(config.DB_PATH)

    while True:
        try:
            start_time = time.time()
            await run_scrape_job(db)
            elapsed = time.time() - start_time
            logger.info(f"Job took {elapsed:.1f} seconds")
        except Exception as exc:
            logger.error(f"Scrape job failed: {exc}", exc_info=True)
            logger.info("Will retry in 1 hour...")
            await asyncio.sleep(3600)
            continue

        # Sleep for 24 hours
        sleep_seconds = 86400
        logger.info(
            f"Sleeping for {sleep_seconds / 3600:.0f} hours until next scrape..."
        )
        await asyncio.sleep(sleep_seconds)


def main():
    """Entry point. Runs once or in loop mode based on env var."""
    loop_mode = os.getenv("LOOP_MODE", "true").lower() == "true"

    if loop_mode:
        logger.info("Running in LOOP mode (24h cycle)")
        asyncio.run(run_loop())
    else:
        logger.info("Running in SINGLE-SHOT mode")
        asyncio.run(run_once())


if __name__ == "__main__":
    main()
