import os
from typing import TYPE_CHECKING, Any, Dict, List, Match, Union

from ._base import BaseDirective, DirectivePlugin

if TYPE_CHECKING:
    from ..block_parser import BlockParser
    from ..core import BaseRenderer, BlockState
    from ..markdown import Markdown


class Include(DirectivePlugin):
    def parse(
        self, block: "BlockParser", m: Match[str], state: "BlockState"
    ) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
        source_file = state.env.get("__file__")
        if not source_file:
            return {"type": "block_error", "raw": "Missing source file"}

        encoding = "utf-8"
        options = self.parse_options(m)
        if options:
            attrs = dict(options)
            if "encoding" in attrs:
                encoding = attrs["encoding"]
        else:
            attrs = {}

        relpath = self.parse_title(m)
        dest = os.path.join(os.path.dirname(source_file), relpath)
        dest = os.path.normpath(dest)

        if os.path.abspath(dest) == os.path.abspath(source_file):
            return {
                "type": "block_error",
                "raw": "Could not include self: " + relpath,
            }

        if not os.path.isfile(dest):
            return {
                "type": "block_error",
                "raw": "Could not find file: " + relpath,
            }

        with open(dest, "rb") as f:
            content = f.read().decode(encoding)

        ext = os.path.splitext(relpath)[1]
        if ext in {".md", ".markdown", ".mkd"}:
            new_state = block.state_cls()
            new_state.env["__file__"] = dest
            new_state.process(content)
            block.parse(new_state)
            return new_state.tokens

        elif ext in {".html", ".xhtml", ".htm"}:
            return {"type": "block_html", "raw": content}

        attrs["filepath"] = dest
        return {
            "type": "include",
            "raw": content,
            "attrs": attrs,
        }

    def __call__(self, directive: BaseDirective, md: "Markdown") -> None:
        directive.register("include", self.parse)
        if md.renderer and md.renderer.NAME == "html":
            md.renderer.register("include", render_html_include)


def render_html_include(renderer: "BaseRenderer", text: str, **attrs: Any) -> str:
    return '<pre class="directive-include">\n' + text + "</pre>\n"
