import os
import uuid
import json
import zipfile
import tarfile
import subprocess


def _parse_url_entry(url_entry):
    if isinstance(url_entry, str):
        try:
            parsed = json.loads(url_entry)
            if isinstance(parsed, dict):
                return parsed
        except (json.JSONDecodeError, TypeError):
            return {"path": url_entry, "name": os.path.splitext(os.path.basename(url_entry))[0], "ext": os.path.splitext(url_entry)[1]}
    elif isinstance(url_entry, dict):
        return url_entry
    return None


def _resolve_input_path(file_object, upload_dir):
    file_path = file_object.get("path", "")
    if not file_path:
        return None, None

    input_path = os.path.join(upload_dir, file_path)
    if not os.path.exists(input_path):
        input_path = file_path
    if not os.path.exists(input_path):
        return None, None

    original_name = file_object.get("name", os.path.splitext(os.path.basename(file_path))[0])
    return input_path, original_name


def _create_zip(input_files, output_path):
    with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zf:
        for filepath, arcname in input_files:
            zf.write(filepath, arcname)


def _create_tar_gz(input_files, output_path):
    with tarfile.open(output_path, 'w:gz') as tf:
        for filepath, arcname in input_files:
            tf.add(filepath, arcname=arcname)


def _create_tar_bz2(input_files, output_path):
    with tarfile.open(output_path, 'w:bz2') as tf:
        for filepath, arcname in input_files:
            tf.add(filepath, arcname=arcname)


def _create_7z(input_files, output_path):
    cmd = ["7z", "a", "-t7z", output_path]
    for filepath, arcname in input_files:
        cmd.append(filepath)

    result = subprocess.run(
        cmd,
        capture_output=True,
        text=True,
        timeout=120
    )

    if result.returncode != 0:
        raise RuntimeError(f"7z compression failed: {result.stderr}")


def convert(urls, target_format, options, config):
    try:
        upload_dir = config.get("UPLOAD_DIR", "static/uploads")
        target_lower = target_format.lower().strip(".")

        supported = {"7z", "tar.bz2", "tar.gz", "zip"}
        if target_lower not in supported:
            return {"error": True, "message": f"Unsupported archive format: {target_format}"}

        input_files = []
        for url_entry in urls:
            file_object = _parse_url_entry(url_entry)
            if not file_object:
                continue

            file_path = file_object.get("path", "")
            if not file_path or file_path == "empty":
                continue

            input_path, original_name = _resolve_input_path(file_object, upload_dir)
            if not input_path:
                continue

            ext = file_object.get("ext", "")
            arcname = f"{original_name}{ext}" if ext else os.path.basename(input_path)
            input_files.append((input_path, arcname))

        if not input_files:
            return {"error": True, "message": "No files were provided for archiving."}

        output_folder = uuid.uuid4().hex
        output_dir = os.path.join(upload_dir, output_folder)
        os.makedirs(output_dir, exist_ok=True)

        archive_name = f"archive.{target_lower}"
        output_path = os.path.join(output_dir, archive_name)

        if target_lower == "zip":
            _create_zip(input_files, output_path)
        elif target_lower == "tar.gz":
            _create_tar_gz(input_files, output_path)
        elif target_lower == "tar.bz2":
            _create_tar_bz2(input_files, output_path)
        elif target_lower == "7z":
            _create_7z(input_files, output_path)

        if not os.path.exists(output_path):
            return {"error": True, "message": "Archive creation failed."}

        return {
            "error": False,
            "results": [output_path],
            "output_path": output_path
        }

    except Exception as e:
        return {"error": True, "message": f"Archive creation failed: {str(e)}"}
