import sys
from typing import Any

from cli.infrastructure.config import ConfigurationError
from cli.infrastructure.formatters.base import PSQL_RECORD_WIDTH, format_scalar_value
from alloy_runtime_sdk.exceptions.errors import (
    AuthenticationError,
    ConflictError,
    ForbiddenError,
    NotFoundError,
    RequestError,
    ServerError,
    ValidationError,
)


def display_error(error: Exception) -> None:
    _write_error_record(_build_error_record(error))


def display_error_record(record: dict[str, Any]) -> None:
    _write_error_record(record)


def display_usage_error(
    message: str,
    *,
    command_path: str,
    hint: str | None = None,
) -> None:
    _write_error_record(
        {
            "Type": "usage_error",
            "Message": message,
            "Hint": hint or f"Run `{command_path} --help`",
        }
    )


def _build_error_record(error: Exception) -> dict[str, Any]:
    if isinstance(error, ConfigurationError):
        return {
            "Type": "configuration_error",
            "Message": str(error),
            "Hint": "Run `alloy login` or set `ALLOY_RUNTIME_API_URL` and `ALLOY_RUNTIME_API_KEY`.",
        }
    if isinstance(error, AuthenticationError):
        return {
            "Type": "authentication_error",
            "Message": str(error),
            "Hint": "Check `ALLOY_RUNTIME_API_KEY` and try again.",
        }
    if isinstance(error, ForbiddenError):
        return {
            "Type": "forbidden",
            "Message": str(error),
            "Hint": "Use an API key with the required permissions.",
        }
    if isinstance(error, NotFoundError):
        return {
            "Type": "not_found",
            "Message": str(error),
            "Hint": "Verify the identifier and try again.",
        }
    if isinstance(error, ValidationError):
        return _build_validation_error_record(error)
    if isinstance(error, ConflictError):
        return {
            "Type": "conflict",
            "Message": str(error),
            "Hint": "Resolve the conflicting resource state and retry.",
        }
    if isinstance(error, RequestError):
        return {
            "Type": "request_error",
            "Message": str(error),
            "Hint": "Check network connectivity and `ALLOY_RUNTIME_API_URL`, then retry.",
        }
    if isinstance(error, ServerError):
        return {
            "Type": "server_error",
            "Message": str(error),
            "Hint": "Retry the request or inspect the server logs.",
        }
    return {
        "Type": "unexpected_error",
        "Message": f"{type(error).__name__}: {error}",
        "Hint": "Re-run the command or inspect the client and server logs.",
    }


def _build_validation_error_record(error: ValidationError) -> dict[str, Any]:
    details: str | None = None
    if error.errors:
        detail_lines: list[str] = []
        for err in error.errors:
            field = " -> ".join(str(loc) for loc in err.get("loc", [])) or "value"
            message = err.get("msg", "Unknown error")
            detail_lines.append(f"{field}: {message}")
        details = "\n".join(detail_lines)

    record: dict[str, Any] = {
        "Type": "validation_error",
        "Message": "Request validation failed" if details else str(error),
        "Hint": "Review the invalid fields and try again.",
    }
    if details:
        record["Details"] = details
    return record


def _write_error_record(record: dict[str, Any]) -> None:
    sys.stderr.write(_render_error_block(record) + "\n")


def _render_error_block(record: dict[str, Any]) -> str:
    header = "-[ ERROR ]"
    lines = [header + ("-" * max(0, PSQL_RECORD_WIDTH - len(header)))]

    label_width = max(11, max((len(label) for label in record), default=0))
    continuation_prefix = (" " * label_width) + "   "

    for label, raw_value in record.items():
        rendered_value = format_scalar_value(raw_value)
        value_lines = rendered_value.split("\n")
        lines.append(f"{label:<{label_width}} : {value_lines[0]}")
        for continuation_line in value_lines[1:]:
            lines.append(f"{continuation_prefix}{continuation_line}")

    lines.append("")
    lines.append("(1 row)")
    return "\n".join(lines)
