#!/usr/bin/env python3
"""
Schema Validator cho batch-index.json

Đảm bảo file index không bị corrupt trước khi cron job đọc.
Chạy TRƯỚC mỗi lần cron job khởi động.

Usage:
    python3 scripts/validate_index.py batch-index.json
"""

import json
import sys
import os
from datetime import datetime


EXPECTED_SCHEMA = {
    "project": str,
    "created": str,
    "batches": list,
}

BATCH_SCHEMA = {
    "id": str,
    "pages": str,
    "status": str,
    "errors": (int, type(None)),
    "accuracy": (str, type(None)),
}

VALID_STATUSES = {"pending", "in_progress", "done", "failed"}


def validate_index(filepath: str) -> tuple[bool, list[str]]:
    """Validate batch-index.json structure."""
    errors = []

    if not os.path.exists(filepath):
        errors.append(f"MISSING: File {filepath} không tồn tại")
        return False, errors

    # 1. Parse JSON
    try:
        with open(filepath, "r", encoding="utf-8") as f:
            data = json.load(f)
    except json.JSONDecodeError as e:
        errors.append(f"PARSE ERROR: {filepath} không phải JSON hợp lệ: {e}")
        return False, errors
    except Exception as e:
        errors.append(f"READ ERROR: {e}")
        return False, errors

    # 2. Check top-level keys
    for key, expected_type in EXPECTED_SCHEMA.items():
        if key not in data:
            errors.append(f"MISSING KEY: Thiếu trường '{key}' ở top-level")
        elif not isinstance(data[key], expected_type):
            errors.append(f"TYPE ERROR: Trường '{key}' phải là {expected_type.__name__}, hiện là {type(data[key]).__name__}")

    # 3. Validate created timestamp
    if "created" in data:
        try:
            # ISO 8601 with timezone
            datetime.fromisoformat(data["created"])
        except (ValueError, TypeError):
            errors.append(f"FORMAT ERROR: 'created' không đúng ISO 8601 format: {data.get('created')}")

    # 4. Validate batches array
    if "batches" not in data or not isinstance(data.get("batches"), list):
        errors.append("MISSING: Trường 'batches' phải là mảng")
        return False, errors

    if len(data["batches"]) == 0:
        errors.append("EMPTY: Mảng 'batches' rỗng")

    seen_ids = set()
    for i, batch in enumerate(data["batches"]):
        prefix = f"batch[{i}]"

        if not isinstance(batch, dict):
            errors.append(f"{prefix}: không phải object")
            continue

        # Check required fields
        for key, expected_type in BATCH_SCHEMA.items():
            if key not in batch:
                errors.append(f"{prefix}: thiếu trường '{key}'")
            else:
                val = batch[key]
                if isinstance(expected_type, tuple):
                    if not isinstance(val, expected_type):
                        type_names = [t.__name__ for t in expected_type]
                        errors.append(f"{prefix}.{key}: phải là {' hoặc '.join(type_names)}, hiện là {type(val).__name__}")
                elif not isinstance(val, expected_type):
                    errors.append(f"{prefix}.{key}: phải là {expected_type.__name__}, hiện là {type(val).__name__}")

        # Validate status
        status = batch.get("status", "")
        if status not in VALID_STATUSES:
            errors.append(f"{prefix}.status: '{status}' không hợp lệ. Hợp lệ: {VALID_STATUSES}")

        # Check duplicate ID
        bid = batch.get("id", "")
        if bid in seen_ids:
            errors.append(f"{prefix}.id: '{bid}' bị trùng lặp")
        seen_ids.add(bid)

        # Validate pages format (e.g., "11-15")
        pages = batch.get("pages", "")
        if pages and "-" not in pages:
            errors.append(f"{prefix}.pages: '{pages}' không đúng format 'XX-YY'")

    return len(errors) == 0, errors


def check_sequence_integrity(filepath: str) -> list[str]:
    """Kiểm tra: không có batch 'pending' nào bị bỏ qua khi batch trước vẫn pending."""
    warnings = []
    with open(filepath, "r", encoding="utf-8") as f:
        data = json.load(f)

    batches = data.get("batches", [])
    found_completed_after_pending = False
    first_pending_idx = None

    for i, batch in enumerate(batches):
        status = batch.get("status", "")
        if status in ("pending", "in_progress") and first_pending_idx is None:
            first_pending_idx = i
        elif status == "done" and first_pending_idx is not None:
            found_completed_after_pending = True
            warnings.append(
                f"SEQUENCE BREAK: batch[{i}] ({batch['id']}) done nhưng batch[{first_pending_idx}] ({batches[first_pending_idx]['id']}) vẫn {batches[first_pending_idx]['status']}"
            )

    return warnings


def get_pending_count(filepath: str) -> int:
    """Đếm số batch pending + in_progress."""
    with open(filepath, "r", encoding="utf-8") as f:
        data = json.load(f)
    return sum(1 for b in data.get("batches", []) if b.get("status") in ("pending", "in_progress"))


def main():
    if len(sys.argv) < 2:
        print("Usage: python3 validate_index.py <batch-index.json>")
        sys.exit(1)

    filepath = sys.argv[1]
    valid, errors = validate_index(filepath)

    if not valid:
        print(f"❌ VALIDATION FAILED ({len(errors)} lỗi):")
        for e in errors:
            print(f"   • {e}")
        sys.exit(1)

    # Check sequence integrity
    warnings = check_sequence_integrity(filepath)
    if warnings:
        print(f"⚠️  WARNINGS ({len(warnings)}):")
        for w in warnings:
            print(f"   • {w}")

    pending = get_pending_count(filepath)
    print(f"✅ VALID — {pending} batch còn pending/in_progress")
    sys.exit(0)


if __name__ == "__main__":
    main()
