"""
One-time backfill: populate magento_file for magento_media_map rows
where magento_entry_id IS NOT NULL and magento_file IS NULL.
CLI: python -m app.jobs.magento_media_backfill_file --connection-id X [--dry-run]
"""

from __future__ import annotations

import argparse
import logging
import sys
import time
from collections import defaultdict

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


def _call_get_media_with_retry(api, sku: str, delay_ms: int, retry_max: int, retry_base_ms: int):
    """Call get_product_media with delay and retry on transient failures."""
    retryable = (429, 500, 502, 503, 504)
    for attempt in range(1, retry_max + 1):
        time.sleep(delay_ms / 1000.0)
        status, entries, err = api.get_product_media(sku)
        if status in retryable and attempt < retry_max:
            sleep_s = (retry_base_ms / 1000.0) * (2 ** (attempt - 1))
            logger.warning(
                "get_product_media %s status=%s (attempt %d/%d), retrying in %.2fs",
                sku, status, attempt, retry_max, sleep_s,
            )
            time.sleep(sleep_s)
            continue
        return status, entries, err
    return None, [], "max retries exceeded"


def run_backfill(
    connection_id: int,
    *,
    dry_run: bool = False,
) -> dict:
    from db.session import get_session
    from db.magento_repositories import (
        SqlAlchemyMagentoConnectionRepository,
        SqlAlchemyMagentoMediaMapRepository,
    )
    from magento.oauth_client import MagentoOAuthClient, build_magento_oauth_kwargs
    from magento.magento_api import MagentoRestClient
    from settings import load_magento_sync_runtime_config

    config = load_magento_sync_runtime_config()
    updated = 0
    skipped = 0
    errors = 0

    with get_session() as session:
        conn_repo = SqlAlchemyMagentoConnectionRepository(session)
        media_repo = SqlAlchemyMagentoMediaMapRepository(session)
        conn = conn_repo.get_for_sync(connection_id)
        if not conn:
            return {"status": "failed", "error": "Connection not found", "updated": 0}

        rows = media_repo.list_rows_missing_magento_file(connection_id)
        if not rows:
            return {"status": "success", "updated": 0, "skipped": 0, "errors": 0}

        oauth = MagentoOAuthClient(**build_magento_oauth_kwargs(conn))
        api = MagentoRestClient(oauth)

        rows_by_sku: dict[str, list[dict]] = defaultdict(list)
        for r in rows:
            rows_by_sku[r["sku"]].append(r)

        for sku, sku_rows in rows_by_sku.items():
            try:
                status, entries, err = _call_get_media_with_retry(
                    api, sku,
                    config.api_delay_ms,
                    config.api_retry_max_attempts,
                    config.api_retry_base_ms,
                )
                if status != 200 or err:
                    logger.error("get_product_media %s: HTTP %s %s", sku, status, err)
                    errors += 1
                    continue
                entry_by_id = {}
                for e in entries:
                    raw_id = e.get("id")
                    if raw_id is None:
                        continue
                    try:
                        entry_by_id[int(raw_id)] = e
                    except (TypeError, ValueError):
                        continue
                for r in sku_rows:
                    eid = r.get("magento_entry_id")
                    if eid is None:
                        continue
                    entry = entry_by_id.get(int(eid))
                    if entry is None:
                        logger.warning("Entry id=%s not found in media for sku=%s", eid, sku)
                        skipped += 1
                        continue
                    magento_file = entry.get("file") or ""
                    if not magento_file:
                        logger.warning("Entry id=%s for sku=%s has empty file", eid, sku)
                        skipped += 1
                        continue
                    if not dry_run:
                        media_repo.update_magento_file_by_id(r["id"], magento_file)
                    updated += 1
                    logger.debug("Backfilled magento_file for row id=%s sku=%s", r["id"], sku)
            except Exception as e:
                logger.exception("Backfill failed for sku=%s: %s", sku, e)
                errors += 1

        if not dry_run:
            session.commit()

    return {
        "status": "success",
        "updated": updated,
        "skipped": skipped,
        "errors": errors,
        "dry_run": dry_run,
    }


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Backfill magento_file for magento_media_map rows with NULL magento_file",
    )
    parser.add_argument("--connection-id", type=int, required=True)
    parser.add_argument("--dry-run", action="store_true")
    args = parser.parse_args()
    result = run_backfill(
        args.connection_id,
        dry_run=args.dry_run,
    )
    if result.get("status") == "failed":
        print(result.get("error", "unknown"), file=sys.stderr)
        return 1
    print(
        f"Backfilled {result.get('updated', 0)} rows, skipped {result.get('skipped', 0)}, errors {result.get('errors', 0)}"
        + (" (dry-run)" if result.get("dry_run") else ""),
    )
    return 0


if __name__ == "__main__":
    sys.exit(main())
