#!/usr/bin/env python3
"""Create a safe local patch workspace from a passed 1C change proposal."""

from __future__ import annotations

import argparse
import hashlib
import json
import re
import shutil
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from check_1c_change_proposal_safety import check, load_json


def slugify(value: str) -> str:
    translit = {
        "а": "a", "б": "b", "в": "v", "г": "g", "д": "d", "е": "e", "ё": "e", "ж": "zh", "з": "z",
        "и": "i", "й": "y", "к": "k", "л": "l", "м": "m", "н": "n", "о": "o", "п": "p", "р": "r",
        "с": "s", "т": "t", "у": "u", "ф": "f", "х": "h", "ц": "c", "ч": "ch", "ш": "sh", "щ": "sch",
        "ъ": "", "ы": "y", "ь": "", "э": "e", "ю": "yu", "я": "ya",
    }
    chars = []
    for char in value.casefold():
        chars.append(translit.get(char, char))
    slug = re.sub(r"[^a-z0-9]+", "-", "".join(chars)).strip("-")
    return slug[:80] or "onec-task"


def sha256_file(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        for chunk in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def unique_targets(proposal: dict[str, Any]) -> list[dict[str, Any]]:
    targets = []
    seen = set()
    for proposal_item in proposal.get("proposals") or []:
        for target in ((proposal_item.get("target_policy") or {}).get("write_candidates") or []):
            path = str(target.get("path") or target.get("module_path") or "")
            if not path or path in seen:
                continue
            seen.add(path)
            targets.append(target)
    return targets


def common_extension_root(paths: list[Path], preferred_extension: str | None) -> Path | None:
    if not preferred_extension:
        return None
    for path in paths:
        parts = list(path.parts)
        lowered = [part.casefold() for part in parts]
        if preferred_extension.casefold() not in lowered:
            continue
        index = lowered.index(preferred_extension.casefold())
        return Path(*parts[: index + 1])
    return None


def relative_target_path(path: Path, root: Path | None) -> Path:
    if root:
        try:
            return path.relative_to(root)
        except ValueError:
            pass
    return Path(path.name)


def write_json(path: Path, data: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")


def copy_targets(targets: list[dict[str, Any]], workspace: Path, root: Path | None) -> list[dict[str, Any]]:
    records = []
    for target in targets:
        src = Path(str(target.get("path") or target.get("module_path")))
        rel = relative_target_path(src, root)
        original_dst = workspace / "original" / rel
        working_dst = workspace / "working" / rel
        original_dst.parent.mkdir(parents=True, exist_ok=True)
        working_dst.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(src, original_dst)
        shutil.copy2(src, working_dst)
        records.append(
            {
                "source_path": str(src),
                "relative_path": str(rel).replace("\\", "/"),
                "original_path": str(original_dst),
                "working_path": str(working_dst),
                "kind": target.get("kind"),
                "name": target.get("name"),
                "origin": target.get("origin"),
                "sha256": sha256_file(src),
                "size": src.stat().st_size,
            }
        )
    return records


def render_readme(proposal: dict[str, Any], safety: dict[str, Any], manifest: dict[str, Any]) -> str:
    task = ((proposal.get("task") or {}).get("text") or "").strip()
    lines = [
        "# 1C Patch Workspace",
        "",
        f"Task: {task}",
        f"Safety passed: `{safety.get('passed')}`",
        "",
        "## Rules",
        "",
        "- Edit files only under `working/`.",
        "- Keep `original/` unchanged; it is used for diff generation.",
        "- Do not write SQL, Config, ConfigSave, ConfigCAS, or production Designer state.",
        "- Validate extension packaging/loading in a disposable base before any production action.",
        "",
        "## Files",
        "",
    ]
    for item in manifest.get("files") or []:
        lines.append(f"- `{item.get('relative_path')}` ({item.get('kind')}, {item.get('origin')})")
    lines.extend(
        [
            "",
            "## Next Commands",
            "",
            "Check workspace integrity:",
            "",
            "```powershell",
            "python scripts/check_1c_patch_workspace_integrity.py --workspace <this-workspace> --output <integrity-json>",
            "```",
            "",
            "Check source freshness before packaging/apply:",
            "",
            "```powershell",
            "python scripts/check_1c_patch_source_freshness.py --workspace <this-workspace> --output <freshness-json>",
            "```",
            "",
            "Validate BSL/Form.xml semantics:",
            "",
            "```powershell",
            "python scripts/validate_1c_patch_workspace_semantics.py --workspace <this-workspace> --output <semantic-validation-json>",
            "```",
            "",
            "Append, replace, or upsert one BSL routine under `working/`:",
            "",
            "```powershell",
            "python scripts/edit_1c_bsl_routine.py --workspace <this-workspace> --relative-path <manifest-bsl-module-relative-path> --operation upsert --routine-text-b64 <utf8-base64-bsl-routine> --output <edit-json>",
            "```",
            "",
            "Append, replace, or upsert one Form.xml command under `working/`:",
            "",
            "```powershell",
            "python scripts/edit_1c_form_command.py --workspace <this-workspace> --relative-path <manifest-form-xml-relative-path> --operation upsert --name <command-name> --title <russian-title> --action <bsl-handler-name> --output <edit-json>",
            "```",
            "",
            "Append, replace, or upsert one visible Form.xml button under `working/`:",
            "",
            "```powershell",
            "python scripts/edit_1c_form_button.py --workspace <this-workspace> --relative-path <manifest-form-xml-relative-path> --operation upsert --parent-name <parent-form-item-name> --name <button-name> --title <russian-title> --command-name <existing-command-name> --output <edit-json>",
            "```",
            "",
            "Preferred atomic workflow for adding a visible form button:",
            "",
            "```powershell",
            "python scripts/add_1c_form_button_workflow.py --workspace <this-workspace> --form-relative-path <manifest-form-xml-relative-path> --bsl-relative-path <manifest-form-module-relative-path> --operation upsert --routine-text-b64 <utf8-base64-bsl-routine> --command-name <command-name> --command-title <russian-command-title> --command-action <bsl-handler-name> --button-parent-name <parent-form-item-name> --button-name <button-name> --button-title <russian-button-title> --output <workflow-json>",
            "```",
            "",
            "Generate diff after editing:",
            "",
            "```powershell",
            "python scripts/diff_1c_patch_workspace.py --workspace <this-workspace> --output <diff-json>",
            "```",
            "",
            "Create review bundle after preflight status is `ready_for_review`:",
            "",
            "```powershell",
            "python scripts/create_1c_patch_bundle.py --workspace <this-workspace> --slug <bundle-slug> --output <bundle-json>",
            "```",
            "",
            "Validate a created review bundle:",
            "",
            "```powershell",
            "python scripts/check_1c_patch_bundle.py --bundle-dir <bundle-dir> --output <bundle-check-json>",
            "```",
            "",
            "Create disposable extension XML staging copy from a valid bundle:",
            "",
            "```powershell",
            "python scripts/create_1c_extension_staging_from_bundle.py --bundle-dir <bundle-dir> --slug <staging-slug> --output <staging-json>",
            "```",
            "",
            "Validate a disposable extension XML staging copy:",
            "",
            "```powershell",
            "python scripts/check_1c_extension_staging.py --staging-dir <staging-dir> --output <staging-check-json>",
            "```",
            "",
            "Validate runner config for disposable 1C validation:",
            "",
            "```powershell",
            "python scripts/check_1c_extension_runner_config.py --config <runner-config-json> --output <runner-config-check-json>",
            "```",
            "",
            "Create a disposable-base validation plan for staging:",
            "",
            "```powershell",
            "python scripts/create_1c_extension_validation_plan.py --staging-dir <staging-dir> --runner-config <runner-config-json> --output <validation-plan-json> --markdown-output <validation-plan-md>",
            "```",
            "",
            "Create pending manual evidence templates from validation plan:",
            "",
            "```powershell",
            "python scripts/create_1c_extension_validation_evidence.py --plan <validation-plan-json> --output <validation-evidence-manifest-json>",
            "```",
            "",
            "Check filled validation evidence:",
            "",
            "```powershell",
            "python scripts/check_1c_extension_validation_evidence.py --plan <validation-plan-json> --output <validation-evidence-check-json>",
            "```",
            "",
            "Aggregate final validation gates for human review:",
            "",
            "```powershell",
            "python scripts/check_1c_extension_validation_release.py --plan <validation-plan-json> --output <validation-release-check-json>",
            "```",
            "",
            "Render final validation report for human review:",
            "",
            "```powershell",
            "python scripts/render_1c_extension_validation_release_markdown.py --release-check <validation-release-check-json> --output <validation-release-md>",
            "```",
            "",
        ]
    )
    return "\n".join(lines)


def create_workspace(proposal_path: Path, output_root: Path, slug: str | None, force: bool) -> dict[str, Any]:
    proposal = load_json(proposal_path)
    safety = check(proposal)
    if not safety.get("passed"):
        raise SystemExit("Proposal safety check failed; workspace not created.")
    task_text = ((proposal.get("task") or {}).get("text") or "").strip()
    workspace_slug = slug or slugify(task_text)
    workspace = output_root / workspace_slug
    if workspace.exists() and not force:
        raise SystemExit(f"Workspace already exists: {workspace}. Use --force to replace.")
    if workspace.exists() and force:
        shutil.rmtree(workspace)
    workspace.mkdir(parents=True, exist_ok=True)

    preferred_extension = None
    for proposal_item in proposal.get("proposals") or []:
        preferred_extension = (proposal_item.get("write_strategy") or {}).get("preferred_extension")
        if preferred_extension:
            break
    targets = unique_targets(proposal)
    source_paths = [Path(str(target.get("path") or target.get("module_path"))) for target in targets]
    extension_root = common_extension_root(source_paths, preferred_extension)
    files = copy_targets(targets, workspace, extension_root)

    manifest = {
        "schema": "onec_patch_workspace_manifest.v1",
        "created_at_utc": datetime.now(timezone.utc).isoformat(),
        "task": proposal.get("task"),
        "source_proposal": str(proposal_path),
        "preferred_extension": preferred_extension,
        "extension_root": str(extension_root) if extension_root else None,
        "workspace": str(workspace),
        "files": files,
        "safety": {
            "schema": safety.get("schema"),
            "passed": safety.get("passed"),
            "counts": safety.get("counts"),
        },
    }
    write_json(workspace / "proposal.json", proposal)
    write_json(workspace / "safety.json", safety)
    write_json(workspace / "manifest.json", manifest)
    (workspace / "README.md").write_text(render_readme(proposal, safety, manifest), encoding="utf-8")
    return {
        "schema": "onec_patch_workspace_creation.v1",
        "workspace": str(workspace),
        "manifest": str(workspace / "manifest.json"),
        "files": len(files),
        "safety_passed": safety.get("passed"),
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Create safe 1C patch workspace.")
    parser.add_argument("--proposal", type=Path, required=True)
    parser.add_argument("--output-root", type=Path, default=Path("reports/1c-sql/upo/patch-workspaces"))
    parser.add_argument("--slug")
    parser.add_argument("--force", action="store_true")
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()

    result = create_workspace(args.proposal, args.output_root, args.slug, args.force)
    output = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
    if args.output:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_text(output, encoding="utf-8")
    print(json.dumps(result, ensure_ascii=False))
    return 0


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