import os
import re
from PIL import Image

from scripts.iib.parsers.model import ImageGenerationInfo, ImageGenerationParams
from scripts.iib.tool import omit, parse_generation_parameters, unique_by


def remove_extra_spaces(text):
    return re.sub(r"\s+", " ", text)


def get_log_file(file_path: str):
    dir = os.path.dirname(file_path)
    with open(os.path.join(dir, "log.html")) as f:
        return f.read()

lora_re = re.compile(r"LoRA \d+", re.IGNORECASE)

class FooocusParser:
    def __init__(self):
        pass

    @classmethod
    def parse(clz, img: Image, file_path):
        if not clz.test(img, file_path):
            raise Exception("The input image does not match the current parser.")
        from lxml import etree

        log = get_log_file(file_path)
        root = etree.HTML(log)
        id = str(os.path.basename(file_path)).replace(".", "_")
        metadata = root.xpath(f'//div[@id="{id}"]/descendant::table[@class="metadata"]')
        tr_elements = metadata[0].xpath(".//tr")
        lora_list = []
        # As a workaround to bypass parsing errors in the parser.
        # https://github.com/jiw0220/stable-diffusion-image-metadata/blob/00b8d42d4d1a536862bba0b07c332bdebb2a0ce5/src/index.ts#L130
        metadata_list_str = "Steps: Unknown , Source Identifier: Fooocus ,"
        params = {"meta": {"Source Identifier": "Fooocus"}}
        for tr in tr_elements:
            label = tr.xpath('.//td[@class="label" or @class="key"]/text()')
            value = tr.xpath('.//td[@class="value"]/text()')
            if label:
                k = label[0]
                v = value[0] if value else "None"
                if k == "Fooocus V2 Expansion":
                    continue
                if k == "Prompt" or k == "Negative Prompt":
                    params[k] = remove_extra_spaces(v.replace("\n", "").strip())
                else:
                    v = v.replace(",", "，")
                    params["meta"][k] = v
                    metadata_list_str += f" {k}: {v},"
                if lora_re.search(k):
                    lora_list.append({ "name": v.strip(), "value": 1 })
        params["meta"]["Model"] = params["meta"]["Base Model"]
        params["meta"]["Size"] = str(params["meta"]["Resolution"]).replace("(", "").replace(")", "").replace("，", " * ")
        metadata_list_str = metadata_list_str.strip()
        info = f"""{params['Prompt']}\nNegative prompt: {params['Negative Prompt']}\n{metadata_list_str}""".strip()
        return ImageGenerationInfo(
            info,
            ImageGenerationParams(
                meta=params["meta"]
                | {
                    "final_width": img.size[0],
                    "final_height": img.size[1],
                },
                pos_prompt=parse_generation_parameters(info)["pos_prompt"],
                extra={
                    "lora": unique_by(lora_list, lambda x: x["name"].lower())
                }
            ),
        )

    @classmethod
    def test(clz, img: Image, file_path: str):
        filename = os.path.basename(file_path)
        try:
            return get_log_file(file_path).find(filename) != -1
        except Exception as e:
            return False
