#!/usr/bin/env python3

import argparse
import re
import subprocess
from pathlib import Path
from datetime import date, datetime

# Base directory of the project
BASE_DIR = Path(__file__).parent.parent
# Specific project files
APPIMAGE_RECIPE = BASE_DIR / "build-aux" / "AppImageBuilder.yml"
ARCH_PKGBUILD = BASE_DIR / "build-aux" / "PKGBUILD"
FEDORA_SPEC = BASE_DIR / "build-aux" / "fedora.spec"
PYPROJECT = BASE_DIR / "pyproject.toml"
APP_PY = BASE_DIR / "portprotonqt" / "app.py"
GITEA_WORKFLOW = BASE_DIR / ".gitea" / "workflows" / "build.yml"
CHANGELOG = BASE_DIR / "CHANGELOG.md"
DEBIAN_CHANGELOG = BASE_DIR / "debian" / "changelog"

def bump_appimage(path: Path, old: str, new: str) -> bool:
    """
    Update only the 'version' field under app_info in AppImageBuilder.yml
    """
    if not path.exists():
        return False
    text = path.read_text(encoding='utf-8')
    pattern = re.compile(r"(?m)^(\s*version:\s*)" + re.escape(old) + r"$")
    new_text, count = pattern.subn(lambda m: m.group(1) + new, text)
    if count:
        path.write_text(new_text, encoding='utf-8')
    return bool(count)

def bump_arch(path: Path, old: str, new: str) -> bool:
    """
    Update pkgver in PKGBUILD
    """
    if not path.exists():
        return False
    text = path.read_text(encoding='utf-8')
    pattern = re.compile(r"(?m)^(pkgver=)" + re.escape(old) + r"$")
    new_text, count = pattern.subn(lambda m: m.group(1) + new, text)
    if count:
        path.write_text(new_text, encoding='utf-8')
    return bool(count)

def bump_fedora(path: Path, old: str, new: str) -> bool:
    """
    Update only the '%global pypi_version' line in fedora.spec
    """
    if not path.exists():
        return False
    text = path.read_text(encoding='utf-8')
    pattern = re.compile(r"(?m)^(%global\s+pypi_version\s+)" + re.escape(old) + r"$")
    new_text, count = pattern.subn(lambda m: m.group(1) + new, text)
    if count:
        path.write_text(new_text, encoding='utf-8')
    return bool(count)

def bump_pyproject(path: Path, old: str, new: str) -> bool:
    """
    Update version in pyproject.toml under [project]
    """
    if not path.exists():
        return False
    text = path.read_text(encoding='utf-8')
    pattern = re.compile(r"(?m)^(version\s*=\s*)\"" + re.escape(old) + r"\"$")
    new_text, count = pattern.subn(lambda m: m.group(1) + f'"{new}"', text)
    if count:
        path.write_text(new_text, encoding='utf-8')
    return bool(count)

def bump_app_py(path: Path, old: str, new: str) -> bool:
    """
    Update __app_version__ in app.py
    """
    if not path.exists():
        return False
    text = path.read_text(encoding='utf-8')
    pattern = re.compile(r"(?m)^(\s*__app_version__\s*=\s*)\"" + re.escape(old) + r"\"$")
    new_text, count = pattern.subn(lambda m: m.group(1) + f'"{new}"', text)
    if count:
        path.write_text(new_text, encoding='utf-8')
    return bool(count)

def bump_workflow(path: Path, old: str, new: str) -> bool:
    """
    Update VERSION in Gitea Actions workflow
    """
    if not path.exists():
        return False
    text = path.read_text(encoding='utf-8')
    pattern = re.compile(r"(?m)^(\s*VERSION:\s*)" + re.escape(old) + r"$")
    new_text, count = pattern.subn(lambda m: m.group(1) + new, text)
    if count:
        path.write_text(new_text, encoding='utf-8')
    return bool(count)

def bump_changelog(path: Path, old: str, new: str) -> bool:
    """
    Update [Unreleased] to [new] - YYYY-MM-DD in CHANGELOG.md
    """
    if not path.exists():
        return False
    text = path.read_text(encoding='utf-8')
    pattern = re.compile(r"(?m)^##\s*\[Unreleased\]$")
    current_date = date.today().strftime('%Y-%m-%d')
    new_text, count = pattern.subn(f"## [{new}] - {current_date}", text)
    if count:
        path.write_text(new_text, encoding='utf-8')
    return bool(count)

def bump_debian_changelog(path: Path, old: str, new: str) -> bool:
    """
    Update debian/changelog with new version
    """
    if not path.exists():
        return False

    # Extract changelog entries from CHANGELOG.md for this version
    changelog_md_path = BASE_DIR / "CHANGELOG.md"
    changelog_entries = []
    changelog_date = None

    if changelog_md_path.exists():
        changelog_text = changelog_md_path.read_text(encoding='utf-8')
        lines = changelog_text.splitlines()

        # Find the section for the new version and extract the date
        start_reading = False
        end_reading = False
        in_contributors_section = False

        for line in lines:
            if line.startswith(f"## [{new}]"):
                # Extract date from line like "## [0.1.9] - 2025-12-08"
                date_match = re.search(r'\[.+\] - (\d{4}-\d{2}-\d{2})', line)
                if date_match:
                    changelog_date_str = date_match.group(1)
                    # Convert to the expected Debian format
                    date_obj = datetime.strptime(changelog_date_str, '%Y-%m-%d')
                    changelog_date = date_obj.strftime('%a, %d %b %Y') + " 00:00:00 +0000"

                start_reading = True
                in_contributors_section = False
                continue
            elif line.startswith("## [") and start_reading:
                end_reading = True
                break
            elif line.strip().lower() == "### contributors":
                # Start of contributors section - skip following lines until next section
                in_contributors_section = True
                continue

            # Skip section headers and contributor sections
            if start_reading and not end_reading and not in_contributors_section:
                stripped_line = line.strip()
                if stripped_line and not line.startswith("#") and not line.startswith("[") and not line.lower().startswith("###"):
                    # Check if this line is a list item with changes
                    if re.match(r'^\s*[*-]\s+', line):
                        # Remove markdown list formatting and add proper Debian format
                        clean_line = re.sub(r'^\s*[*-]\s+', '  * ', line.rstrip())
                        # Remove common markdown formatting like backticks
                        clean_line = re.sub(r'`([^`]+)`', r'"\1"', clean_line)  # Replace `code` with "code"
                        changelog_entries.append(clean_line)
                    # Also include lines that are sub-items (indented changes)
                    elif line.startswith("    ") and re.match(r'^\s*[*-]\s+', line[4:]):
                        clean_line = re.sub(r'^\s*[*-]\s+', '  * ', line[4:].rstrip())
                        clean_line = "  " + clean_line  # Add extra indentation
                        # Remove common markdown formatting
                        clean_line = re.sub(r'`([^`]+)`', r'"\1"', clean_line)
                        changelog_entries.append(clean_line)

    # If no specific entries found for this version, use generic message
    if not changelog_entries:
        changelog_entries = ["  * New upstream release"]

    # Use changelog date if available, otherwise use current time
    current_time = changelog_date if changelog_date else datetime.now().strftime('%a, %d %b %Y %H:%M:%S +0000')

    # Read the existing changelog to get maintainer info and other fields
    text = path.read_text(encoding='utf-8')

    # If the file is empty or doesn't contain proper maintainer info, use a default
    lines = text.splitlines()

    if not lines or not any(line.startswith(" -- ") for line in lines):
        # Create a default changelog entry with proper format
        package_name = "portprotonqt"
        new_version_line = f"{package_name} ({new}-1) unstable; urgency=medium"

        # Default maintainer info from the original file
        default_maintainer = "Boris Yumankulov <boria138@altlinux.org>"
        maintainer_line = f" -- {default_maintainer}  {current_time}"

        new_content = new_version_line + "\n\n" + "\n".join(changelog_entries) + "\n\n" + maintainer_line + "\n"
    else:
        # Extract the header template from the current first entry
        header_parts = []
        entry_end_index = 0

        for i, line in enumerate(lines):
            header_parts.append(line)
            if line.startswith(" -- "):
                entry_end_index = i + 1
                break

        # Construct new changelog entry
        new_entry_lines = []

        if header_parts:
            # Parse the first line to extract package name (before the version)
            first_line = header_parts[0]
            # Extract package name by getting everything before the opening parenthesis
            if '(' in first_line:
                package_name = first_line.split('(')[0].strip()
                new_version_line = f"{package_name} ({new}-1) unstable; urgency=medium"
            else:
                # Fallback: if no parentheses found, use a default format
                new_version_line = f"portprotonqt ({new}-1) unstable; urgency=medium"

            new_entry_lines.append(new_version_line)

            # Add the changelog entries
            new_entry_lines.extend(changelog_entries)

            # Add the maintainer info and timestamp
            for j in range(1, len(header_parts)):
                if header_parts[j].startswith(" -- "):
                    # Extract the maintainer information (everything after "-- ")
                    maintainer_part = header_parts[j][4:]  # Remove leading " -- "
                    # Extract only the name and email, ignore timestamp
                    maintainer_info = maintainer_part.split('  ')[0].strip()
                    new_entry_lines.append(f" -- {maintainer_info}  {current_time}")
                elif not header_parts[j].startswith("  *"):  # Skip existing changes since we added new ones
                    new_entry_lines.append(header_parts[j])

        # Reconstruct the file with new entry at the top followed by the rest
        new_content = '\n'.join(new_entry_lines) + '\n' + '\n'.join(lines[entry_end_index:])

    path.write_text(new_content, encoding='utf-8')
    return True


def main():
    parser = argparse.ArgumentParser(description='Bump project version in specific files')
    parser.add_argument('old', help='Old version string')
    parser.add_argument('new', help='New version string')
    args = parser.parse_args()
    old, new = args.old, args.new

    tasks = [
        (APPIMAGE_RECIPE, bump_appimage),
        (ARCH_PKGBUILD, bump_arch),
        (FEDORA_SPEC, bump_fedora),
        (PYPROJECT, bump_pyproject),
        (APP_PY, bump_app_py),
        (GITEA_WORKFLOW, bump_workflow),
        (CHANGELOG, bump_changelog),
        (DEBIAN_CHANGELOG, bump_debian_changelog)
    ]

    updated = []
    for path, func in tasks:
        if func(path, old, new):
            updated.append(path.relative_to(BASE_DIR))

    if updated:
        print(f"Updated version from {old} to {new} in {len(updated)} files:")
        for p in sorted(updated):
            print(f" - {p}")

        try:
            subprocess.run(["uv", "lock"], check=True)
            print("Regenerated uv.lock")
        except subprocess.CalledProcessError as e:
            print(f"Failed to regenerate uv.lock: {e}")
    else:
        print(f"No occurrences of version {old} found in specified files.")

if __name__ == '__main__':
    main()
