import re
import subprocess
import sys
import traceback
from importlib import resources
from pathlib import Path

from jinja2 import Template
from watchfiles import watch

from ssb_pubmd import templates
from ssb_pubmd.adapters.content_parser import MimirContentParser
from ssb_pubmd.adapters.document_processor import PandocDocumentProcessor
from ssb_pubmd.adapters.publish_client import PublishClient
from ssb_pubmd.adapters.publish_client import get_publish_client
from ssb_pubmd.adapters.storage import LocalFileStorage
from ssb_pubmd.config import Config
from ssb_pubmd.domain.document_publisher import sync_document


def run_cli(system_arguments: list[str], config: Config) -> None:
    match system_arguments:
        case [_, "create", article_name]:
            _create_template(article_name, config)
        case [_, "preview", article_name]:
            _preview(article_name, config)
        case _:
            print("Usage:")
            print("  ssb-pubmd create FILENAME")
            print("  ssb-pubmd preview ARTICLE_NAME")
            sys.exit(1)


def _create_template(article_name: str, config: Config) -> None:
    name = _sanitize_name(article_name)
    project_folder = Path.cwd() / name

    if project_folder.exists():
        print(f"Error: Folder '{name}' already exists")
        sys.exit(1)

    project_folder.mkdir()

    template_destination = project_folder / f"{name}.qmd"

    with resources.open_text(templates, "template.qmd") as file:
        raw_template = file.read()

    rendered_template = Template(raw_template).render(
        filepath=template_destination,
        cms_admin_url=config.publish_base_url + config.publish_admin_path,
        project_folder=template_destination.parent.name,
        filename=template_destination.name,
    )
    template_destination.write_text(rendered_template)
    print(f"Created project folder {name}.")
    print(f"Created template file {name}/{name}.qmd")
    print("Open the template file for further instructions.")

def _sanitize_name(name: str) -> str:
    name = name.lower()
    name = re.sub(r"[ _]+", "-", name)
    name = re.sub(r"[^a-z0-9-]", "", name)
    name = re.sub(r"-{2,}", "-", name)
    return name.strip("-")

def _preview(file_path: str, config: Config) -> None:
    if Path(file_path).suffix != ".qmd":
        print("Only Quarto Markdown (.qmd) files are supported.")
        sys.exit(1)

    publish_client = get_publish_client(config)

    _sync_updated_file(file_path, publish_client)

    print("Watching for file changes...")
    for changes in watch(file_path):
        print("Found changes.")
        _sync_updated_file(file_path, publish_client)
        print("Watching for file changes...")

def _sync_updated_file(file_path: str, publish_client: PublishClient) -> None:
    print("Syncing updated document...")
    try:
        preview_url = _sync_quarto_file(file_path, publish_client)
        print(f"Content synced successfully. Preview URL: {preview_url}")
    except Exception:
        print("Failed to sync content. Full error log:")
        traceback.print_exc()

def _sync_quarto_file(file_path: str, publish_client: PublishClient) -> str:
    pandoc_document = _quarto_to_pandoc(file_path)
    adapters = (
        PandocDocumentProcessor(),
        MimirContentParser(),
        LocalFileStorage(project_folder=Path(file_path).parent),
        publish_client,
    )
    return sync_document(pandoc_document, *adapters)


def _quarto_to_pandoc(file_path: str) -> str:
    try:
        result = subprocess.run(
            [
                "quarto",
                "render",
                file_path,
                "--to",
                "json",
                "-M",
                "include:false",
                "--output",
                "-",
            ],
            text=True,
            capture_output=True,
            check=True,
        )
        return result.stdout
    except subprocess.CalledProcessError as err:
        message = err.stderr.strip() or "Quarto failed with no error output"
        raise RuntimeError(f"Quarto render failed:\n{message}") from err
