from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

from trae_workspace_tool.models import ValidationIssue
from trae_workspace_tool.registry import DEFAULT_PACK, DEFAULT_PROFILE, selected_assets

REQUIRED_FILES = [asset.relative_path for asset in selected_assets()]


@dataclass(frozen=True)
class InstallResult:
    created: list[str]
    overwritten: list[str]
    skipped: list[str]


@dataclass(frozen=True)
class DoctorReport:
    ok: bool
    present: list[str]
    missing: list[str]
    invalid: list[ValidationIssue]


def install_workspace(
    target: Path,
    force: bool = False,
    profile: str = DEFAULT_PROFILE,
    pack: str = DEFAULT_PACK,
) -> InstallResult:
    root = target.expanduser().resolve()
    root.mkdir(parents=True, exist_ok=True)
    created: list[str] = []
    overwritten: list[str] = []
    skipped: list[str] = []

    for asset in selected_assets(profile, pack):
        path = root / asset.relative_path
        if path.exists() and not force:
            skipped.append(asset.relative_path)
            continue
        path.parent.mkdir(parents=True, exist_ok=True)
        existed = path.exists()
        path.write_text(asset.content, encoding="utf-8")
        if asset.executable:
            path.chmod(0o755)
        if existed:
            overwritten.append(asset.relative_path)
        else:
            created.append(asset.relative_path)

    return InstallResult(created=created, overwritten=overwritten, skipped=skipped)


def inspect_workspace(
    target: Path,
    profile: str = DEFAULT_PROFILE,
    pack: str = DEFAULT_PACK,
) -> DoctorReport:
    root = target.expanduser().resolve()
    present: list[str] = []
    missing: list[str] = []
    invalid: list[ValidationIssue] = []

    for asset in selected_assets(profile, pack):
        path = root / asset.relative_path
        if not path.exists():
            missing.append(asset.relative_path)
            continue

        present.append(asset.relative_path)
        content = path.read_text(encoding="utf-8")
        for validator in asset.validators:
            if reason := validator(content):
                invalid.append(ValidationIssue(asset.relative_path, reason))
        if asset.executable and not _is_executable(path):
            invalid.append(ValidationIssue(asset.relative_path, "script is not executable"))

    return DoctorReport(ok=not missing and not invalid, present=present, missing=missing, invalid=invalid)


def _is_executable(path: Path) -> bool:
    return bool(path.stat().st_mode & 0o111)
