name: Build Linux Release

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version name for the release (e.g., 1.0.0)'
        required: true
        type: string
  workflow_call:
    inputs:
      version:
        description: 'Version name for the release (e.g., 1.0.0)'
        required: true
        type: string

permissions:
  contents: write

env:
  PYTHON_VERSION: '3.13'

jobs:
  build-linux:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v6
      with:
        fetch-depth: 0

    - name: Get version
      id: get_version
      shell: bash
      run: |
        version="${{ inputs.version || github.event.inputs.version }}"
        echo "Extracted version: $version"
        echo "VERSION=$version" >> "$GITHUB_OUTPUT"

    - name: Setup Python
      uses: actions/setup-python@v6
      with:
        python-version: ${{ env.PYTHON_VERSION }}

    - name: Cache Python dependencies
      uses: actions/cache@v4
      with:
        path: |
          venv
          ~/.cache/pip
        key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml') }}
        restore-keys: |
          ${{ runner.os }}-pip-

    - name: Install system dependencies
      shell: bash
      run: |
        sudo apt-get update -y
        # Tools commonly required by cx_Freeze on Linux packaging
        sudo apt-get install -y --no-install-recommends \
          rpm alien fakeroot patchelf desktop-file-utils file xz-utils zsync

    - name: Create virtual environment and install dependencies
      shell: bash
      run: |
        python -m venv venv
        source venv/bin/activate
        python -m pip install --upgrade pip
        pip install --no-cache-dir .
        pip install --no-cache-dir cx_Freeze

    - name: Prepare build variables
      shell: bash
      run: |
        version="${{ steps.get_version.outputs.VERSION }}"
        echo "VERSION=$version" >> "$GITHUB_ENV"
        arch="$(uname -m)" # x86_64 or aarch64
        echo "ARCH=$arch" >> "$GITHUB_ENV"
        echo "Prepared build variables for version: $version on $(uname -a)"

    - name: Create cx_Freeze setup script (Linux)
      shell: bash
      run: |
        # Create entry point script
        cat > ytsage_entry.py <<'ENTRY'
        from ytsage.main import main
        if __name__ == "__main__":
            main()
        ENTRY

        cat > setup_cxfreeze.py <<'PY'
        import os
        import sys
        from pathlib import Path
        from cx_Freeze import setup, Executable

        version = os.environ.get("VERSION", "0.0.0")

        # Prepare include_files list
        include_files_list = [
            ("ytsage/assets/Icon", "lib/assets/Icon"),
            ("ytsage/assets/sound", "lib/assets/sound"),
            ("ytsage/languages", "lib/languages"),
            ("branding/icons", "lib/assets/branding/icons"),
            ("ytsage.desktop", "share/applications/ytsage.desktop"),
            ("branding/icons/icon.png", "share/pixmaps/ytsage.png"),
        ]

        build_exe_options = dict(
            optimize=2,
            silent=True,
            packages=[
                "ytsage",
                "PySide6.QtCore",
                "PySide6.QtGui",
                "PySide6.QtWidgets",
                "PySide6.QtMultimedia",
                "PySide6.QtNetwork",
                "PySide6.QtDBus",
                "PySide6.QtSvg",
                "requests",
                "PIL",
                "packaging",
                "markdown",
                "loguru",
                "setuptools",
            ],
            zip_include_packages=[
                "PySide6",
                "shiboken6",
                "requests",
                "PIL",
                "packaging",
            ],
            excludes=[
                "PySide6.QtBluetooth",
                "PySide6.QtOpenGL",
                "PySide6.QtPrintSupport",
                "PySide6.QtTest",
                "PySide6.QtXml",
                "PySide6.QtSql",
                "PySide6.QtHelp",
                "PySide6.QtQml",
                "PySide6.QtQuick",
                "PySide6.QtWebEngineCore",
                "PIL.ImageDraw",
                "PIL.ImageFont",
                "numpy",
                "scipy",
                "wx",
                "pandas",
                "tkinter",
                "yt_dlp",
                "unittest",
                "test",
                "tests",
                "pydoc",
                "doctest",
                "email",
            ],
            include_files=include_files_list,
            # Bundle all dependencies - avoid system library references
            bin_includes=[],
            bin_excludes=[],
            # Important: include system libs to avoid external dependencies
            replace_paths=[("*", "")],
        )

        executables = [
            Executable(
                script="ytsage_entry.py",
                target_name="ytsage",
                icon="branding/icons/icon.png",
            )
        ]

        setup(
            name="ytsage",
            version=version,
            description="YTSage",
            options={
                "build_exe": build_exe_options,
                # AppImage packaging
                "bdist_appimage": {
                    # If ends with .AppImage, use verbatim file name
                    "target_name": f"ytsage-v{version}.AppImage",
                },
            },
            executables=executables,
        )
        PY

    - name: Create desktop entry
      shell: bash
      run: |
        cat > ytsage.desktop <<'DESKTOP'
        [Desktop Entry]
        Type=Application
        Name=YTSage
        Comment=YouTube downloader
        Exec=/usr/bin/ytsage %U
        Icon=ytsage
        Terminal=false
        Categories=AudioVideo;Network;Utility;
        StartupWMClass=YTSage
        DESKTOP

    - name: Build AppImage and RPM
      shell: bash
      run: |
        set -e
        source venv/bin/activate

        # Ensure a clean build and build_exe first with -OO
        python -OO setup_cxfreeze.py build_exe

        # Clean unnecessary files from build directory before packaging
        build_dir=$(ls -d build/exe.* 2>/dev/null | head -n1 || true)
        
        if [ -n "$build_dir" ]; then
          echo "Trimming files in $build_dir..."
          lib_dir="$build_dir/lib"
          
          if [ -d "$lib_dir" ]; then
            # 1. Remove screenshots
            if [ -d "$lib_dir/assets/branding/screenshots" ]; then
               rm -rf "$lib_dir/assets/branding/screenshots"
               echo "Removed screenshots folder"
            fi

            # 2. Remove unused Qt translations
            if [ -d "$lib_dir/PySide6/translations" ]; then
               rm -rf "$lib_dir/PySide6/translations"
               echo "Removed Qt translations"
            fi

            # 3. Remove unused Qt plugins
            plugins_dir="$lib_dir/PySide6/plugins"
            if [ -d "$plugins_dir" ]; then
                for plugin in designer pdf sql help qml quick webengine bluetooth opengl printsupport test xml; do
                  if [ -d "$plugins_dir/$plugin" ]; then
                    rm -rf "$plugins_dir/$plugin"
                    echo "Removed plugin: $plugin"
                  fi
                done
                # Specific cleanups (imageformats)
                rm -f "$plugins_dir/imageformats/libqpdf.so"
            fi

            # 4. Remove bloat shared libraries (Recursively finds in lib/ and lib/PySide6/)
            # Matches libQt6Qml.so.6, Qt6Qml.abi3.so, etc.
            echo "Removing bloat Qt libraries..."
            find "$lib_dir" -name "*Qt6Web*" -delete
            find "$lib_dir" -name "*Qt6Pdf*" -delete
            find "$lib_dir" -name "*Qt6Qml*" -delete
            find "$lib_dir" -name "*Qt6Quick*" -delete
            find "$lib_dir" -name "*Qt6VirtualKeyboard*" -delete
            find "$lib_dir" -name "*Qt6OpenGL*" -delete 

            # 5. Remove build tools and unused python modules
            for tool in setuptools wheel pkg_resources _distutils_hack curses _pyrepl; do
               rm -rf "$lib_dir/$tool"
               echo "Removed build tool/module: $tool"
            done
          else
             echo "Warning: lib directory not found inside build_dir"
          fi
        else
          echo "Warning: Build directory not found, cannot trim files"
        fi

        # Build AppImage manually to avoid cx_Freeze rebuilding (and untrimming) the artifacts
        echo "Creating AppImage manually..."
        mkdir -p dist
        
        # Download appimagetool
        wget -q -O appimagetool https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage
        chmod +x appimagetool

        # Setup AppDir structure (Root layout matches cx_Freeze build output exactly)
        rm -rf AppDir
        mkdir -p AppDir
        
        # Copy trimmed build content DIRECTLY to AppDir root
        # This keeps 'lib' adjacent to 'ytsage', preventing 'ModuleNotFoundError: encodings'
        cp -r "$build_dir"/* AppDir/
        
        # Setup metadata
        cp ytsage.desktop AppDir/
        # Fix Exec path for AppImage context (binary is in root, not /usr/bin)
        sed -i 's|Exec=/usr/bin/ytsage|Exec=ytsage|g' AppDir/ytsage.desktop
        cp branding/icons/icon.png AppDir/ytsage.png
        # .DirIcon for file managers
        cp branding/icons/icon.png AppDir/.DirIcon 
        
        # Create AppRun as a script to correctly set environment
        cat > AppDir/AppRun <<'APPRUN'
        #!/bin/sh
        SELF=$(readlink -f "$0")
        HERE=$(dirname "$SELF")
        export LD_LIBRARY_PATH="$HERE/lib:$LD_LIBRARY_PATH"
        exec "$HERE/ytsage" "$@"
        APPRUN
        chmod +x AppDir/AppRun
        
        # Build AppImage (using --appimage-extract-and-run to avoid FUSE issues in CI)
        # We explicitly set ARCH for appimagetool
        ARCH=x86_64 ./appimagetool --appimage-extract-and-run AppDir "dist/ytsage-v${version}.AppImage"

        # Build RPM manually with proper spec file
        version="${{ steps.get_version.outputs.VERSION }}"
        arch_uname="${ARCH}"
        workspace_root="$(pwd)"
        build_dir_abs="${workspace_root}/${build_dir}"
        
        # Create custom RPM spec file that doesn't auto-detect dependencies
        mkdir -p build/rpm/{BUILD,RPMS,SOURCES,SPECS,SRPMS}
        
        cat > build/rpm/SPECS/ytsage.spec <<SPEC
        %global __requires_exclude_from ^/opt/ytsage/.*$
        %global __provides_exclude_from ^/opt/ytsage/.*$
        %define _build_id_links none
        %undefine _missing_build_ids_terminate_build
        
        Name:           ytsage
        Version:        ${version//-/.}
        Release:        1%{?dist}
        Summary:        YTSage - A modern YouTube downloader
        License:        MIT
        URL:            https://github.com/oop7/YTSage
        AutoReqProv:    no
        
        %description
        YTSage is a modern YouTube downloader with a PySide6 interface.
        Download videos, extract audio, fetch subtitles, and more.
        All dependencies are bundled within the package.
        
        %install
        rm -rf %{buildroot}
        mkdir -p %{buildroot}/opt/ytsage
        mkdir -p %{buildroot}/usr/bin
        mkdir -p %{buildroot}/usr/share/applications
        mkdir -p %{buildroot}/usr/share/pixmaps
        
        # Copy application files
        cp -a ${build_dir_abs}/* %{buildroot}/opt/ytsage/
        
        # Create wrapper script
        cat > %{buildroot}/usr/bin/ytsage <<'WRAPPER'
        #!/usr/bin/env bash
        set -euo pipefail
        APPDIR="/opt/ytsage"
        cd "\$APPDIR"
        exec "\$APPDIR/ytsage" "\$@"
        WRAPPER
        chmod 0755 %{buildroot}/usr/bin/ytsage
        
        # Install desktop file and icon
        install -m 0644 ${workspace_root}/ytsage.desktop %{buildroot}/usr/share/applications/ytsage.desktop
        install -m 0644 ${workspace_root}/branding/icons/icon.png %{buildroot}/usr/share/pixmaps/ytsage.png
        
        %files
        /opt/ytsage
        /usr/bin/ytsage
        /usr/share/applications/ytsage.desktop
        /usr/share/pixmaps/ytsage.png
        
        %post
        if command -v update-desktop-database >/dev/null 2>&1; then
          update-desktop-database -q /usr/share/applications || true
        fi
        
        %postun
        if command -v update-desktop-database >/dev/null 2>&1; then
          update-desktop-database -q /usr/share/applications || true
        fi
        
        %changelog
        * $(date +'%a %b %d %Y') YTSage Maintainers <noreply@example.com> - ${version//-/.}-1
        - Release ${version}
        SPEC
        
        # Build the RPM
        rpmbuild -bb build/rpm/SPECS/ytsage.spec \
          --define "_topdir $(pwd)/build/rpm" \
          --buildroot "$(pwd)/build/rpm/BUILDROOT"
        
        # Copy RPM to dist
        mkdir -p dist
        find build/rpm/RPMS -name "*.rpm" -exec cp {} dist/ \;

        echo "Post-build directory listing:"
        echo "-- dist --"; ls -lah dist || true
        echo "-- build --"; ls -lah build || true

    - name: Build native DEB package
      shell: bash
      run: |
        set -e
        version="${{ steps.get_version.outputs.VERSION }}"
        arch_uname="${ARCH}"
        # Map uname -m to Debian arch names
        case "$arch_uname" in
          x86_64) deb_arch=amd64 ;;
          aarch64) deb_arch=arm64 ;;
          *) deb_arch="$arch_uname" ;;
        esac

        # Locate build output
        build_dir=$(ls -d build/exe.* 2>/dev/null | head -n1)
        if [ -z "$build_dir" ]; then
          echo "Error: build/exe.* directory not found" >&2
          exit 1
        fi

        # Staging root
        pkgroot=deb_pkg
        rm -rf "$pkgroot"
        mkdir -p "$pkgroot/DEBIAN" \
                 "$pkgroot/usr/bin" \
                 "$pkgroot/usr/share/applications" \
                 "$pkgroot/usr/share/pixmaps" \
                 "$pkgroot/opt/ytsage"

        # Install application payload under /opt/ytsage
        cp -a "$build_dir"/* "$pkgroot/opt/ytsage/"

        # Wrapper to ensure correct working directory
        cat > "$pkgroot/usr/bin/ytsage" <<'WRAP'
        #!/usr/bin/env bash
        set -euo pipefail
        APPDIR="/opt/ytsage"
        cd "$APPDIR"
        exec "$APPDIR/ytsage" "$@"
        WRAP
        chmod 0755 "$pkgroot/usr/bin/ytsage"

        # Desktop file and icon
        install -m 0644 ytsage.desktop "$pkgroot/usr/share/applications/ytsage.desktop"
        install -m 0644 branding/icons/icon.png "$pkgroot/usr/share/pixmaps/ytsage.png"

        # Control file
        cat > "$pkgroot/DEBIAN/control" <<CONTROL
        Package: ytsage
        Version: ${version}
        Section: utils
        Priority: optional
        Architecture: ${deb_arch}
        Maintainer: YTSage Maintainers <noreply@example.com>
        Homepage: https://github.com/oop7/YTSage
        Description: YTSage - A modern YouTube downloader with a PySide6 interface
         Download videos, extract audio, fetch subtitles, and more.
        CONTROL

        # Post-install script to refresh desktop/menu caches (best-effort)
        cat > "$pkgroot/DEBIAN/postinst" <<'POSTINST'
        #!/bin/sh
        set -e
        if command -v update-desktop-database >/dev/null 2>&1; then
          update-desktop-database -q || true
        fi
        if command -v gtk-update-icon-cache >/dev/null 2>&1; then
          gtk-update-icon-cache -q /usr/share/icons/hicolor || true
        fi
        exit 0
        POSTINST
        chmod 0755 "$pkgroot/DEBIAN/postinst"

        # Triggers so Debian updates caches
        cat > "$pkgroot/DEBIAN/triggers" <<'TRIGGERS'
        interest-noawait /usr/share/applications
        interest-noawait /usr/share/icons/hicolor
        TRIGGERS

        # Build the deb
        deb_out="YTSage-v${version}-${deb_arch}.deb"
        dpkg-deb --build "$pkgroot" "$deb_out"

        mkdir -p artifacts
        mv "$deb_out" artifacts/
        echo "Built native DEB: artifacts/$deb_out"

    - name: Build Flatpak Bundle
      shell: bash
      run: |
        version="${{ steps.get_version.outputs.VERSION }}"
        
        # Install flatpak-builder
        sudo apt-get install -y flatpak-builder
        flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

        # Define manifest ID
        APP_ID="io.github.oop7.YTSage"
        
        # Create a specific desktop file for Flatpak
        # - Exec must be 'ytsage' (in path), not /usr/bin/ytsage
        # - Icon must match the App ID (io.github.oop7.YTSage)
        cat > flatpak.desktop <<EOF
        [Desktop Entry]
        Type=Application
        Name=YTSage
        Comment=YouTube downloader
        Exec=ytsage 
        Icon=$APP_ID
        Terminal=false
        Categories=AudioVideo;Network;Utility;
        StartupWMClass=YTSage
        EOF

        # Create manifest manually
        cat > ytsage_flatpak.json <<EOF
        {
          "app-id": "$APP_ID",
          "runtime": "org.kde.Platform",
          "runtime-version": "6.10",
          "sdk": "org.kde.Sdk",
          "command": "ytsage",
          "finish-args": [
            "--share=ipc",
            "--socket=x11",
            "--socket=wayland",
            "--socket=pulseaudio",
            "--device=dri",
            "--share=network",
            "--filesystem=host",
            "--env=YTDLP_APP_BIN_PATH=/var/data/yt-dlp",
            "--env=DENO_APP_BIN_PATH=/var/data/deno"
          ],
          "modules": [
            {
              "name": "ytsage",
              "buildsystem": "simple",
              "build-commands": [
                "mkdir -p /app/bin /app/share/ytsage",
                "cp -r dist/ytsage-v${version}-*/* /app/share/ytsage/",
                "ln -s /app/share/ytsage/ytsage /app/bin/ytsage",
                "install -D flatpak.desktop /app/share/applications/$APP_ID.desktop",
                "install -D branding/icons/icon.png /app/share/icons/hicolor/128x128/apps/$APP_ID.png"
              ],
              "sources": [
                {
                  "type": "dir",
                  "path": "."
                }
              ]
            }
          ]
        }
        EOF

        # Build the Flatpak
        # Note: In a real environment, building from source is preferred.
        # Here we are "bundling" the already built binaries from the previous step (cx_Freeze) for simplicity in CI.
        # This requires the 'dist/' folder to be populated by the previous 'Create cx_Freeze setup script' + build steps.
        # Wait, the previous steps built into 'build/exe.linux-...' not 'dist/'. Let's find it.
        
        build_dir=$(ls -d build/exe.* 2>/dev/null | head -n1)
        if [ -z "$build_dir" ]; then
          echo "Error: build/exe.* directory not found for Flatpak build" >&2
          exit 1
        fi
        
        # Move build dir to a fixed location for the manifest source to catch
        mkdir -p dist/ytsage-v${version}-flatpak
        cp -r "$build_dir"/* "dist/ytsage-v${version}-flatpak/"
        
        # Install runtime/sdk
        flatpak install -y --user flathub org.kde.Platform//6.10 org.kde.Sdk//6.10

        # Build
        flatpak-builder --user --install-deps-from=flathub --repo=repo --force-clean build-flatpak ytsage_flatpak.json
        
        # Bundle
        flatpak build-bundle repo artifacts/YTSage-v${version}-x86_64.flatpak $APP_ID
        echo "Built Flatpak: artifacts/YTSage-v${version}-x86_64.flatpak"

    - name: Package and prepare release artifacts (.AppImage, .rpm, .deb)
      shell: bash
      run: |
        version="${{ steps.get_version.outputs.VERSION }}"
        mkdir -p artifacts

        # Copy AppImage
        appimage_src=$(ls dist/ytsage-v*.AppImage 2>/dev/null | head -n1 || true)
        if [ -n "$appimage_src" ]; then
          cp "$appimage_src" "artifacts/YTSage-v${version}-${ARCH}.AppImage"
          echo "Copied AppImage: $appimage_src -> artifacts/YTSage-v${version}-${ARCH}.AppImage"
        else
          echo "Warning: No .AppImage found in dist/"
        fi

        # Copy RPM (normalize name)
        rpm_src=$(ls dist/ytsage-*.rpm 2>/dev/null | head -n1 || true)
        if [ -n "$rpm_src" ]; then
          cp "$rpm_src" "artifacts/YTSage-v${version}-${ARCH}.rpm"
          echo "Copied RPM: $rpm_src -> artifacts/YTSage-v${version}-${ARCH}.rpm"
        else
          echo "Warning: No .rpm found in dist/"
        fi

        # DEB is already staged in artifacts by the native packaging step
        ls -1 artifacts/*.deb 2>/dev/null || echo "Warning: No .deb found in artifacts/"

        # Check for Flatpak
        ls -1 artifacts/*.flatpak 2>/dev/null || echo "Warning: No .flatpak found in artifacts/"

        echo "Final artifacts:"
        ls -lh artifacts || true

    - name: Create/Update draft release
      uses: softprops/action-gh-release@v2
      with:
        tag_name: v${{ steps.get_version.outputs.VERSION }}
        name: YTSage v${{ steps.get_version.outputs.VERSION }}
        draft: true
        prerelease: false
        append_body: true
        fail_on_unmatched_files: false
        body: |
          # YTSage v${{ steps.get_version.outputs.VERSION }}

          **Release Date**: ${{ github.event.head_commit.timestamp }}
        files: |
          artifacts/*
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
