from pathlib import Path
from typing import Any, Dict, Iterator, List, Optional

import yaml

from truefoundry.cli.console import console
from truefoundry.deploy.lib.clients.servicefoundry_client import (
    ServiceFoundryServiceClient,
)
from truefoundry.deploy.lib.messages import PROMPT_DELETING_MANIFEST
from truefoundry.deploy.lib.model.entity import DeleteResult, File, ManifestLike
from truefoundry.pydantic_v1 import ValidationError


def _delete_manifest(
    manifest: Dict[str, Any],
    client: Optional[ServiceFoundryServiceClient] = None,
    filename: Optional[str] = None,
    index: Optional[int] = None,
) -> DeleteResult:
    client = client or ServiceFoundryServiceClient()

    file_metadata = ""
    if index is not None:
        file_metadata += f" at index {index}"
    if filename:
        file_metadata += f" from file {filename}"

    try:
        parsed_manifest = ManifestLike.parse_obj(manifest)
    except ValidationError as ex:
        return DeleteResult(
            success=False,
            message=f"Failed to parse manifest{file_metadata}. Error: {ex}",
        )

    manifest_name = ""
    if parsed_manifest.name:
        manifest_name = f" {parsed_manifest.name}"

    try:
        client.delete(parsed_manifest.dict(exclude_unset=True, exclude_none=True))

        return DeleteResult(
            success=True,
            message=(
                f"Successfully deleted resource manifest{manifest_name} of type {parsed_manifest.type}."
            ),
        )
    except Exception as ex:
        return DeleteResult(
            success=False,
            message=(
                f"Failed to delete resource manifest{manifest_name} of type {parsed_manifest.type}. Error: {ex}."
            ),
        )


def delete_manifest(
    manifest: Dict[str, Any],
    client: Optional[ServiceFoundryServiceClient] = None,
) -> DeleteResult:
    return _delete_manifest(manifest=manifest, client=client)


def delete_manifest_resources(
    filename: str,
    manifests_it: List[Dict[str, Any]],
    client: Optional[ServiceFoundryServiceClient] = None,
) -> Iterator[DeleteResult]:
    for index, manifest in enumerate(manifests_it):
        if not isinstance(manifest, dict):
            yield DeleteResult(
                success=False,
                message=f"Failed to delete resource manifest at index {index} from file {filename}. Error: A manifest must be a dict, got type {type(manifest)}",
            )
            continue

        yield _delete_manifest(
            manifest=manifest,
            client=client,
            filename=filename,
            index=index,
        )


def delete_manifest_file(
    filepath: str,
    client: Optional[ServiceFoundryServiceClient] = None,
) -> Iterator[DeleteResult]:
    client = client or ServiceFoundryServiceClient()
    filename = Path(filepath).name
    try:
        with open(filepath, "r") as f:
            manifests_it = list(yaml.safe_load_all(f))
    except Exception as ex:
        yield DeleteResult(
            success=False,
            message=f"Failed to read file {filepath} as a valid YAML file. Error: {ex}",
        )
    else:
        yield from delete_manifest_resources(
            filename=filename, manifests_it=manifests_it, client=client
        )


def delete_manifest_files(
    files: List[File],
    sf_server_client: Optional[ServiceFoundryServiceClient] = None,
    read_content_from_file: bool = True,
) -> List[DeleteResult]:
    delete_results: List[DeleteResult] = []
    sf_server_client = sf_server_client or ServiceFoundryServiceClient()
    for file_info in files:
        file_path = file_info.path
        delete_result_it: Iterator[DeleteResult] = []
        if read_content_from_file:
            delete_result_it = delete_manifest_file(file_path, sf_server_client)
        else:
            try:
                manifests_it = [yaml.safe_load(file_info.content)]
            except Exception:
                continue
            delete_result_it = delete_manifest_resources(
                file_path, manifests_it, sf_server_client
            )

        with console.status(PROMPT_DELETING_MANIFEST.format(file_path), spinner="dots"):
            for delete_result in delete_result_it:
                if delete_result.success:
                    console.print(f"[green]\u2714 {delete_result.message}[/]")
                else:
                    console.print(f"[red]\u2718 {delete_result.message}[/]")

                delete_results.append(delete_result)
    return delete_results
