#!/usr/bin/env python3
import argparse
import json
import re
from pathlib import Path
from typing import Any, Dict, List


HARNESS_ROOT = Path(__file__).resolve().parent
DEFAULT_SCHEMA = HARNESS_ROOT / "schemas/task-v1.schema.json"


def _label(path: str) -> str:
    return path or "task"


def _validate(value: Any, schema: Dict[str, Any], path: str = "") -> List[str]:
    errors: List[str] = []
    expected_type = schema.get("type")

    if expected_type == "object":
        if not isinstance(value, dict):
            return [f"{_label(path)} must be an object"]

        for field in schema.get("required", []):
            if field not in value:
                field_path = f"{path}.{field}" if path else field
                errors.append(f"{field_path} is required")

        properties = schema.get("properties", {})
        if schema.get("additionalProperties") is False:
            for field in value:
                if field not in properties:
                    field_path = f"{path}.{field}" if path else field
                    errors.append(f"{field_path} is not allowed")

        for field, field_schema in properties.items():
            if field in value:
                field_path = f"{path}.{field}" if path else field
                errors.extend(_validate(value[field], field_schema, field_path))
        return errors

    if expected_type == "array":
        if not isinstance(value, list):
            return [f"{_label(path)} must be an array"]
        minimum = schema.get("minItems")
        if minimum is not None and len(value) < minimum:
            errors.append(f"{_label(path)} must contain at least {minimum} item")
        item_schema = schema.get("items")
        if item_schema:
            for index, item in enumerate(value):
                errors.extend(_validate(item, item_schema, f"{path}[{index}]"))

    if expected_type == "string":
        if not isinstance(value, str):
            return [f"{_label(path)} must be a string"]
        minimum = schema.get("minLength")
        if minimum is not None and len(value) < minimum:
            errors.append(f"{_label(path)} must contain at least {minimum} character")
        pattern = schema.get("pattern")
        if pattern and re.fullmatch(pattern, value) is None:
            errors.append(f"{_label(path)} does not match the required pattern")

    if "const" in schema and value != schema["const"]:
        errors.append(f"{_label(path)} must equal {schema['const']}")

    if "enum" in schema and value not in schema["enum"]:
        allowed = ", ".join(schema["enum"])
        errors.append(f"{_label(path)} must be one of: {allowed}")

    return errors


def validate_file(task_path: Path, schema_path: Path = DEFAULT_SCHEMA) -> List[str]:
    task = json.loads(Path(task_path).read_text(encoding="utf-8"))
    schema = json.loads(Path(schema_path).read_text(encoding="utf-8"))
    return _validate(task, schema)


def main() -> int:
    parser = argparse.ArgumentParser(description="Validate a Task Contract v1 JSON file.")
    parser.add_argument("task", type=Path)
    args = parser.parse_args()

    errors = validate_file(args.task)
    if errors:
        for error in errors:
            print(f"FAIL {error}")
        return 1

    print(f"PASS {args.task}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
