"""Build CodeExplorer instances from repository archives."""

from __future__ import annotations

import asyncio
import io
import os
import tempfile
import time
import zipfile
from pathlib import Path

from code_reviewer.adaptors.repository import RepositoryHandler
from code_reviewer.adaptors.repository.github import GitHubRepositoryHandler
from code_reviewer.adaptors.repository.gitlab import GitLabRepositoryHandler
from code_reviewer.config import Config
from code_reviewer.errors import PlatformError
from code_reviewer.explorer import CodeExplorer
from code_reviewer.logging_config import BaseLogger
from code_reviewer.workflows.context import LinkedRepoInfo

logger = BaseLogger("explorer", "builder")


async def _build_explorer_from_archive(
    archive: bytes,
    name: str,
) -> CodeExplorer | None:
    """Build a CodeExplorer from archive bytes."""
    try:
        temp_dir = tempfile.mkdtemp(prefix=f"reviewate_{name}_")

        with zipfile.ZipFile(io.BytesIO(archive)) as zf:
            zf.extractall(temp_dir)

        # Archives extract with a prefix like "owner-repo-commit/"
        source_root = temp_dir
        entries = os.listdir(temp_dir)
        if len(entries) == 1:
            potential_root = os.path.join(temp_dir, entries[0])
            if os.path.isdir(potential_root):
                source_root = potential_root

        return CodeExplorer(Path(source_root))
    except Exception as e:
        logger.warning(f"Failed to build CodeExplorer for {name}: {e}")
        return None


async def build_code_explorer(
    config: Config,
    repository: RepositoryHandler,
    repo: str,
    source_branch: str,
    linked_repos: list[LinkedRepoInfo] | None = None,
) -> tuple[CodeExplorer | None, list[LinkedRepoInfo]]:
    """Build code explorer from repository archives.

    Returns:
        Tuple of (explorer, linked_repos_info) where linked_repos_info
        contains only the repos that were successfully built.
    """

    async def build_primary() -> CodeExplorer | None:
        try:
            start = time.time()
            logger.info("Building CodeExplorer from source branch")
            archive = await repository.fetch_archive(repo, source_branch)
            explorer = await _build_explorer_from_archive(archive, "primary")
            if explorer:
                stats = explorer.get_stats()
                logger.info(
                    f"Built CodeExplorer with {stats['total_files']} files "
                    f"in {time.time() - start:.2f}s"
                )
            return explorer
        except PlatformError:
            raise  # Let caller handle with context (branch deleted, auth failed, etc.)
        except Exception as e:
            logger.warning(f"Failed to build CodeExplorer: {e}, continuing without exploration")
            return None

    async def fetch_linked_archive(
        linked: LinkedRepoInfo,
    ) -> tuple[LinkedRepoInfo, bytes | None]:
        """Fetch archive for a linked repository."""
        try:
            linked_handler: RepositoryHandler
            if linked["provider"] == "github":
                github_token = config.github_api_token
                api_url = (
                    linked["provider_url"] + "/api/v3"
                    if "api.github.com" not in linked["provider_url"]
                    else "https://api.github.com"
                )
                if github_token:
                    linked_handler = GitHubRepositoryHandler.from_token(
                        api_url=api_url,
                        token=github_token,
                    )
                else:
                    linked_handler = GitHubRepositoryHandler.from_cli(api_url=api_url)
            elif linked["provider"] == "gitlab":
                gitlab_token = config.gitlab_api_token
                provider_url = linked["provider_url"].rstrip("/")
                api_url = (
                    provider_url if provider_url.endswith("/api/v4") else f"{provider_url}/api/v4"
                )
                if gitlab_token:
                    linked_handler = GitLabRepositoryHandler.from_token(
                        api_url=api_url,
                        token=gitlab_token,
                    )
                else:
                    linked_handler = GitLabRepositoryHandler.from_cli(api_url=api_url)
            else:
                logger.warning(f"Unknown provider for linked repo: {linked['provider']}")
                return linked, None

            ref = linked["branch"] or "HEAD"
            logger.info(f"Fetching linked repo archive: {linked['repo_path']}@{ref}")
            archive = await linked_handler.fetch_archive(linked["repo_path"], ref)
            await linked_handler.close()
            return linked, archive
        except Exception as e:
            logger.warning(f"Failed to fetch linked repo {linked['repo_path']}: {e}")
            return linked, None

    # Fetch primary + linked repos in parallel
    tasks: list = [build_primary()]
    if linked_repos:
        for linked in linked_repos:
            tasks.append(fetch_linked_archive(linked))

    results = await asyncio.gather(*tasks)
    primary_explorer: CodeExplorer | None = results[0]
    linked_repos_info: list[LinkedRepoInfo] = []

    # Add linked repos to the primary explorer
    if linked_repos and primary_explorer:
        for linked_result in results[1:]:
            linked_info, archive = linked_result
            if archive:
                short_name = linked_info["name"]
                linked_explorer = await _build_explorer_from_archive(archive, short_name)
                if linked_explorer:
                    primary_explorer.add_linked(
                        short_name,
                        linked_explorer._root_path,
                        display_name=linked_info.get("display_name"),
                    )
                    linked_repos_info.append(linked_info)
                    logger.info(f"Added linked repo [{short_name}]")

        if linked_repos_info:
            total_stats = primary_explorer.get_stats()
            logger.info(
                f"CodeExplorer ready: {total_stats['total_files']} total files "
                f"(primary + {len(linked_repos_info)} linked repos)"
            )

    return primary_explorer, linked_repos_info
