"""Helper script to rebuild virtualenv.py from virtualenv_support."""

from __future__ import annotations

import codecs
import locale
import os
import re
from typing import TYPE_CHECKING, NoReturn
from zlib import crc32 as _crc32

if TYPE_CHECKING:
    from pathlib import Path


def crc32(data: str) -> int:
    """Python version idempotent."""
    return _crc32(data.encode()) & 0xFFFFFFFF


here = os.path.realpath(os.path.dirname(__file__))
script = os.path.realpath(os.path.join(here, "..", "src", "virtualenv.py"))

gzip = codecs.lookup("zlib")
b64 = codecs.lookup("base64")

file_regex = re.compile(r'# file (.*?)\n([a-zA-Z][a-zA-Z0-9_]+) = convert\(\n {4}"""\n(.*?)"""\n\)', re.DOTALL)
file_template = '# file {filename}\n{variable} = convert(\n    """\n{data}"""\n)'


def rebuild(script_path: Path) -> None:
    encoding = (
        locale.getencoding() if hasattr(locale, "getencoding") else locale.getpreferredencoding(do_setlocale=False)
    )
    with script_path.open(encoding=encoding) as current_fh:
        script_content = current_fh.read()
    script_parts = []
    match_end = 0
    next_match = None
    count, did_update = 0, False
    for count, next_match in enumerate(file_regex.finditer(script_content)):  # ruff:ignore[unused-loop-control-variable]
        script_parts += [script_content[match_end : next_match.start()]]
        match_end = next_match.end()
        filename, variable_name, previous_encoded = next_match.group(1), next_match.group(2), next_match.group(3)
        differ, content = handle_file(next_match.group(0), filename, variable_name, previous_encoded)
        script_parts.append(content)
        if differ:
            did_update = True

    script_parts += [script_content[match_end:]]
    new_content = "".join(script_parts)

    report(1 if not count or did_update else 0, new_content, next_match, script_content, script_path)


def handle_file(previous_content: str, filename: str, variable_name: str, previous_encoded: str) -> tuple[bool, str]:
    print(f"Found file {filename}")  # ruff:ignore[print]
    current_path = os.path.realpath(os.path.join(here, "..", "src", "virtualenv_embedded", filename))
    _, file_type = os.path.splitext(current_path)
    keep_line_ending = file_type == ".bat"
    with open(current_path, encoding="utf-8", newline="" if keep_line_ending else None) as current_fh:
        current_text = current_fh.read()
    current_crc = crc32(current_text)
    current_encoded = b64.encode(gzip.encode(current_text.encode())[0])[0].decode()
    if current_encoded == previous_encoded:
        print(f"  File up to date (crc: {current_crc:08x})")  # ruff:ignore[print]
        return False, previous_content
    # Else: content has changed
    previous_text = gzip.decode(b64.decode(previous_encoded.encode())[0])[0].decode()
    previous_crc = crc32(previous_text)
    print(f"  Content changed (crc: {previous_crc:08x} -> {current_crc:08x})")  # ruff:ignore[print]
    new_part = file_template.format(filename=filename, variable=variable_name, data=current_encoded)
    return True, new_part


def report(exit_code: int, new: str, next_match: re.Match[str] | None, current: str, script_path: Path) -> NoReturn:
    if new != current:
        print("Content updated; overwriting... ", end="")  # ruff:ignore[print]
        script_path.write_bytes(new)
        print("done.")  # ruff:ignore[print]
    else:
        print("No changes in content")  # ruff:ignore[print]
    if next_match is None:
        print("No variables were matched/found")  # ruff:ignore[print]
    raise SystemExit(exit_code)


if __name__ == "__main__":
    rebuild(script)
