# migration.py
#
# Copyright 2025 Pavel Baksy
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
#
# SPDX-License-Identifier: GPL-3.0-or-later

import json
import logging
import re
import shutil
from pathlib import Path

logger = logging.getLogger(__name__)


def _sanitize_name(name: str) -> str:
    s = name.lower().strip()
    s = re.sub(r'[^\w\s-]', '', s)
    s = re.sub(r'[\s_]+', '-', s)
    s = re.sub(r'-+', '-', s)
    return s.strip('-') or 'unnamed'


def _unique_name(used: set, base: str) -> str:
    if base not in used:
        return base
    i = 2
    while f"{base}-{i}" in used:
        i += 1
    return f"{base}-{i}"


def needs_migration(projects_dir: Path, legacy_file: Path) -> bool:
    return legacy_file.exists() and not projects_dir.exists()


def run_migration(legacy_file: Path, projects_dir: Path) -> bool:
    """
    Read requests.json, write new per-file directory structure.
    Atomic: writes to a tmp dir first, then renames.
    On success renames requests.json → requests.json.bak.
    On failure leaves no partial state and returns False.
    """
    tmp_dir = projects_dir.parent / 'projects.tmp'

    try:
        with open(legacy_file, 'r') as f:
            data = json.load(f)

        projects = data.get('projects', [])

        if tmp_dir.exists():
            shutil.rmtree(tmp_dir)
        tmp_dir.mkdir(parents=True)

        used_project_dirs: set = set()

        for project in projects:
            proj_base = _sanitize_name(project.get('name', ''))
            proj_dir_name = _unique_name(used_project_dirs, proj_base)
            used_project_dirs.add(proj_dir_name)

            proj_dir = tmp_dir / proj_dir_name
            proj_dir.mkdir()

            requests = project.get('requests', [])
            request_order = []
            used_req_basenames: set = set()

            for req in requests:
                req_base = _sanitize_name(req.get('name', ''))
                req_basename = _unique_name(used_req_basenames, req_base)
                used_req_basenames.add(req_basename)
                req_filename = req_basename + '.json'
                request_order.append(req_filename)

                with open(proj_dir / req_filename, 'w') as f:
                    json.dump(req, f, indent=2)

            project_meta = {k: v for k, v in project.items() if k != 'requests'}
            project_meta['request_order'] = request_order

            with open(proj_dir / '_project.json', 'w') as f:
                json.dump(project_meta, f, indent=2)

        # Atomic rename
        tmp_dir.rename(projects_dir)

        bak_file = legacy_file.parent / (legacy_file.name + '.bak')
        legacy_file.rename(bak_file)

        logger.info("Migration successful: %d projects migrated, backup at %s", len(projects), bak_file)
        return True

    except Exception as e:
        logger.error("Migration failed: %s", e)
        try:
            if tmp_dir.exists():
                shutil.rmtree(tmp_dir)
        except Exception:
            pass
        return False
