"""
Main detector class for license and copyright detection.
"""

import logging
import time
from pathlib import Path
from typing import List, Optional

from .models import Config, CopyrightInfo, DetectedLicense, DetectionResult
from .input_processor import InputProcessor

logger = logging.getLogger(__name__)


class LicenseCopyrightDetector:
    """
    Main class for detecting licenses and copyright information in source code.
    """

    def __init__(self, config: Optional[Config] = None):
        """
        Initialize the license and copyright detector.

        Args:
            config: Optional configuration object
        """
        self.config = config or Config()
        self.input_processor = InputProcessor()

        # Lazy load components as needed
        self._license_detector = None
        self._copyright_extractor = None
        self._spdx_data = None

        # Initialize cache if cache_dir is configured
        self._cache = None
        if self.config.cache_dir:
            from ..utils.cache_manager import CacheManager

            self._cache = CacheManager(cache_dir=self.config.cache_dir)

    @property
    def license_detector(self):
        """Lazy load license detector."""
        if self._license_detector is None:
            from ..detectors.license_detector import LicenseDetector

            self._license_detector = LicenseDetector(self.config)
        return self._license_detector

    @property
    def copyright_extractor(self):
        """Lazy load copyright extractor."""
        if self._copyright_extractor is None:
            from ..extractors.copyright_extractor import CopyrightExtractor

            self._copyright_extractor = CopyrightExtractor(self.config)
        return self._copyright_extractor

    def process_local_path(self, path: str, extract_archives: bool = True) -> DetectionResult:
        """
        Process a local source code directory or file.

        Args:
            path: Path to local directory or file
            extract_archives: Whether to extract and scan archives

        Returns:
            DetectionResult object
        """
        # Check cache first
        if self._cache:
            cached_data = self._cache.get(path)
            if cached_data:
                logger.info(f"Using cached result for {path}")
                # Reconstruct DetectionResult from cached data
                result = DetectionResult(path=path)

                cached_data["licenses"] = [
                    DetectedLicense(**license) for license in cached_data["licenses"]
                ]

                cached_data["copyrights"] = [
                    CopyrightInfo(**copyright) for copyright in cached_data["copyrights"]
                ]

                result.__dict__.update(cached_data)
                return result

        start_time = time.time()

        # Validate path
        is_valid, path_obj, error = self.input_processor.validate_local_path(path)
        logger.debug(f"Path validation: is_valid={is_valid}, path_obj={path_obj}, error={error}")

        result = DetectionResult(path=str(path), package_name=Path(path).name)

        if not is_valid:
            result.errors.append(error)
            logger.warning(f"Path validation failed: {error}")
            return result

        try:
            logger.info(f"Processing local path: {path}")
            logger.debug(
                f"Path object: {path_obj}, is_file: {path_obj.is_file()}, extract_archives: {extract_archives}"
            )

            # Check if it's an archive and extract if needed
            if extract_archives and path_obj.is_file():
                from ..utils.archive_extractor import ArchiveExtractor

                extractor = ArchiveExtractor(max_depth=self.config.max_extraction_depth)

                if extractor.is_archive(path_obj):
                    logger.info(f"Detected archive file: {path_obj}")
                    with extractor:
                        extracted_dir = extractor.extract_archive(path_obj)
                        if extracted_dir:
                            logger.info(f"Extracted archive to: {extracted_dir}")
                            self._process_local_path(extracted_dir, result)
                        else:
                            logger.warning(f"Failed to extract archive: {path_obj}")
                            self._process_local_path(path_obj, result)
                else:
                    logger.debug(f"Not an archive, processing as regular file: {path_obj}")
                    self._process_local_path(path_obj, result)
            else:
                self._process_local_path(path_obj, result)

        except Exception as e:
            logger.error(f"Error processing {path}: {e}")
            result.errors.append(str(e))

        finally:
            result.processing_time = time.time() - start_time

        # Store in cache if enabled
        if self._cache and not result.errors:
            self._cache.set(path, result.to_dict())

        return result

    def extract_package_metadata(self, path: str) -> DetectionResult:
        """
        Fast-path API for extracting license information from package metadata files only.
        This method skips full text analysis and only extracts from structured metadata.

        Supports:
        - package.json (Node.js)
        - pyproject.toml, setup.py, setup.cfg (Python)
        - pom.xml (Maven/Java)
        - Cargo.toml (Rust)
        - *.gemspec (Ruby)
        - *.nuspec (NuGet/.NET)
        - composer.json (PHP)
        - build.gradle (Gradle/Java)

        Args:
            path: Path to package metadata file or directory containing metadata files

        Returns:
            DetectionResult with licenses extracted from metadata only
        """
        start_time = time.time()

        # Validate path
        is_valid, path_obj, error = self.input_processor.validate_local_path(path)

        result = DetectionResult(path=str(path), package_name=Path(path).name)

        if not is_valid:
            result.errors.append(error)
            return result

        try:
            from ..detectors.license_detector import LicenseDetector

            detector = LicenseDetector(self.config)

            # List of package metadata filenames to look for
            metadata_files = [
                "package.json",
                "pyproject.toml",
                "setup.py",
                "setup.cfg",
                "pom.xml",
                "Cargo.toml",
                "composer.json",
                "build.gradle",
            ]

            files_to_scan = []

            if path_obj.is_file():
                # Single file mode
                files_to_scan = [path_obj]
            else:
                # Directory mode - find metadata files
                for metadata_file in metadata_files:
                    candidate = path_obj / metadata_file
                    if candidate.exists() and candidate.is_file():
                        files_to_scan.append(candidate)

                # Also check for .gemspec and .nuspec files
                for pattern in ["*.gemspec", "*.nuspec"]:
                    files_to_scan.extend(path_obj.glob(pattern))

            # Extract metadata from each file
            for file_path in files_to_scan:
                try:
                    content = self.input_processor.read_text_file(file_path)
                    if content:
                        metadata_licenses = detector._extract_package_metadata(content, file_path)
                        result.licenses.extend(metadata_licenses)
                except Exception as e:
                    logger.debug(f"Error reading {file_path}: {e}")

            # Calculate confidence scores
            if result.licenses:
                result.confidence_scores["license"] = max(l.confidence for l in result.licenses)
            else:
                result.confidence_scores["license"] = 0.0

        except Exception as e:
            logger.error(f"Error extracting metadata from {path}: {e}")
            result.errors.append(str(e))
        finally:
            result.processing_time = time.time() - start_time

        return result

    def _process_local_path(self, path: Path, result: DetectionResult):
        """
        Process a local directory or file.

        Args:
            path: Path to local directory or file
            result: DetectionResult to populate
        """
        # Detect licenses
        logger.debug(
            f"_process_local_path called with: {path} (is_file: {path.is_file()}, exists: {path.exists()})"
        )
        licenses = self.license_detector.detect_licenses(path)
        logger.debug(f"License detector returned {len(licenses)} licenses")
        result.licenses.extend(licenses)

        # Extract copyright information
        copyrights = self.copyright_extractor.extract_copyrights(path)
        result.copyrights.extend(copyrights)

        # Calculate confidence scores
        if result.licenses:
            result.confidence_scores["license"] = max(l.confidence for l in result.licenses)
        else:
            result.confidence_scores["license"] = 0.0

        if result.copyrights:
            result.confidence_scores["copyright"] = max(c.confidence for c in result.copyrights)
        else:
            result.confidence_scores["copyright"] = 0.0

        logger.debug(
            f"Found {len(result.licenses)} license(s) and {len(result.copyrights)} copyright(s)"
        )

    def generate_evidence(
        self, results: List[DetectionResult], detail_level: str = "detailed"
    ) -> str:
        """
        Generate evidence showing file-to-license mappings.

        Args:
            results: List of attribution results
            detail_level: Evidence detail level ('minimal', 'summary', 'detailed', 'full')

        Returns:
            Evidence as JSON string
        """
        from ..formatters.evidence_formatter import EvidenceFormatter

        formatter = EvidenceFormatter()
        return formatter.format(results, detail_level=detail_level)

    def generate_kissbom(self, results: List[DetectionResult]) -> str:
        """
        Generate KissBOM (Keep It Simple Software Bill of Materials) output.

        Args:
            results: List of detection results

        Returns:
            KissBOM as JSON string
        """
        from ..formatters.kissbom_formatter import KissBOMFormatter

        formatter = KissBOMFormatter()
        return formatter.format(results)

    def generate_cyclonedx(self, results: List[DetectionResult], format_type: str = "json") -> str:
        """
        Generate CycloneDX SBOM output.

        Args:
            results: List of detection results
            format_type: Output format ("json" or "xml")

        Returns:
            CycloneDX SBOM as string
        """
        from ..formatters.cyclonedx_formatter import CycloneDXFormatter

        formatter = CycloneDXFormatter()
        return formatter.format(results, format_type)
