"""Service for analyzing billing drop composition and CSV row counting."""

import gzip
import csv
from datetime import datetime
from typing import List, Dict, Optional, Tuple
from pathlib import Path

from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn

from ..s3_data_service import S3DataService
from .models import DropComposition, DatasetContribution, DropInfo
from .upload_tracking_service import UploadTrackingService


class BillingDropService:
    """Service for analyzing billing drop composition and counting CSV rows."""

    def __init__(self, s3_service: S3DataService, upload_tracking_service: UploadTrackingService):
        self.s3_service = s3_service
        self.upload_tracking_service = upload_tracking_service
        self.console = Console()

    def get_drop_composition(self, organization: str, provider: str, date_range: str) -> Optional[DropComposition]:
        """
        Analyze the composition of a billing drop by examining upload history and CSV files.

        This is the slow operation that counts actual CSV rows and provides detailed
        composition analysis.

        Args:
            organization: Organization name
            provider: Provider (AWS, GCP, Azure)
            date_range: Date range in format YYYYMMDD-YYYYMMDD (now treated as month)

        Returns:
            DropComposition with detailed analysis or None if drop doesn't exist
        """
        try:
            # Get active drop ID from manifest
            active_drop_id = self._get_active_drop_id_from_manifest(organization, provider, date_range)
            if not active_drop_id:
                return None

            # Get contributing upload events
            upload_events = self.upload_tracking_service.get_drop_upload_composition(
                organization, provider, date_range
            )

            # Show performance warning
            self.console.print("⚠️  [yellow]Computing CSV billing row counts (this may take 2-5 minutes)[/yellow]")

            # Compute CSV row counts with progress tracking
            total_csv_rows, total_files, dataset_contributions = self._compute_csv_composition_with_progress(
                organization, provider, date_range, upload_events
            )

            return DropComposition(
                organization=organization,
                provider=provider,
                date_range=date_range,
                active_drop_id=active_drop_id,
                contributing_datasets=dataset_contributions,
                total_csv_billing_rows=total_csv_rows,
                total_files_processed=total_files
            )

        except Exception:
            return None

    def list_organization_drops(self, organization: str) -> List[DropInfo]:
        """
        List all billing drops for an organization.

        Returns:
            List of DropInfo objects with basic drop information
        """
        org_path = f"piedpiper-custom-billing/{organization}/"
        drops = []

        try:
            # List provider folders
            provider_folders = self.s3_service.list_folders(org_path)

            for provider_folder in provider_folders:
                provider = provider_folder.rstrip('/').split('/')[-1]

                if not provider:
                    continue

                # List month folders for this provider
                month_folders = self.s3_service.list_folders(provider_folder)

                for month_folder in month_folders:
                    month = month_folder.rstrip('/').split('/')[-1]

                    if not month:
                        continue

                    # Get active drop ID from manifest.json
                    active_drop_id = self._get_active_drop_id_from_manifest(organization, provider, month)

                    if active_drop_id:
                        # Get last upload time (use current approach for now)
                        last_upload = self._get_last_upload_time(organization, provider, month)

                        drop_info = DropInfo(
                            organization=organization,
                            provider=provider.upper(),
                            date_range=month,  # Using month instead of date_range
                            active_drop_id=active_drop_id,
                            last_upload=last_upload
                        )
                        drops.append(drop_info)

        except Exception:
            pass

        # Sort by last upload time, most recent first
        return sorted(drops, key=lambda x: x.last_upload or datetime.min, reverse=True)

    def _get_active_drop_id(self, organization: str, provider: str, month: str) -> Optional[str]:
        """Get the active drop ID for a billing drop (legacy method, use _get_active_drop_id_from_manifest instead)."""
        # Delegate to the manifest-based approach
        return self._get_active_drop_id_from_manifest(organization, provider, month)

    def _get_active_drop_id_from_manifest(self, organization: str, provider: str, month: str) -> Optional[str]:
        """Get the active drop ID from manifest.json file."""
        manifest_path = f"piedpiper-custom-billing/{organization}/{provider}/{month}/manifest.json"

        try:
            # Read the manifest.json file
            manifest_content = self.s3_service.read_text_file(manifest_path)

            # Parse JSON
            import json
            manifest_data = json.loads(manifest_content)

            # Extract active drop ID
            # The manifest structure uses "current_drop_id" as the key
            active_drop_id = manifest_data.get("current_drop_id") or manifest_data.get("active_drop_id") or manifest_data.get("active_drop")

            return active_drop_id if active_drop_id else None

        except Exception:
            # If manifest doesn't exist or is invalid, return None
            return None

    def _get_last_upload_time(self, organization: str, provider: str, month: str) -> Optional[datetime]:
        """Get the last upload time for a drop from tracking data."""
        try:
            upload_events = self.upload_tracking_service.get_drop_upload_composition(
                organization, provider, month
            )

            if upload_events and len(upload_events) > 0:
                return max(event.uploaded_at for event in upload_events)

        except Exception:
            pass

        return None

    def _compute_csv_composition_with_progress(
        self,
        organization: str,
        provider: str,
        month: str,
        upload_events: List
    ) -> Tuple[int, int, List[DatasetContribution]]:
        """
        Compute CSV composition with progress indicators.

        Returns:
            (total_csv_rows, total_files_processed, dataset_contributions)
        """
        # Get active drop ID from manifest
        active_drop_id = self._get_active_drop_id_from_manifest(organization, provider, month)

        if not active_drop_id:
            return 0, 0, []

        # Build path to the billing drop data
        full_drop_path = f"piedpiper-custom-billing/{organization}/{provider}/{month}/{active_drop_id}/"

        try:
            # Get all CSV.gz files directly in the drop folder
            csv_files = self._get_csv_gz_files(full_drop_path)

            total_csv_rows = 0
            total_files_processed = 0
            dataset_contributions = []

            # Create dataset contributions from upload events
            for upload_event in upload_events:
                try:
                    # Get basic metadata for description
                    from .dataset_metadata_service import DatasetMetadataService
                    metadata_service = DatasetMetadataService(self.s3_service)
                    basic_metadata = metadata_service.get_basic_metadata(upload_event.dataset_id)

                    dataset_contribution = DatasetContribution(
                        dataset_id=upload_event.dataset_id,
                        description=basic_metadata.description,
                        upload_event=upload_event,
                        csv_billing_rows=0,  # Will be computed below
                        file_count=0         # Will be computed below
                    )
                    dataset_contributions.append(dataset_contribution)
                except Exception:
                    continue

            # Progress tracking for CSV counting
            with Progress(
                SpinnerColumn(),
                TextColumn("[progress.description]{task.description}"),
                BarColumn(),
                TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
                console=self.console,
            ) as progress:

                task = progress.add_task("Processing CSV files...", total=len(csv_files))

                for csv_file in csv_files:
                    file_name = csv_file.split('/')[-1]
                    progress.update(task, description=f"Processing {file_name}...")

                    try:
                        row_count = self._count_csv_gz_rows(csv_file)
                        total_csv_rows += row_count
                        total_files_processed += 1

                        # For simplicity, attribute rows evenly across contributing datasets
                        # In practice, you might want more sophisticated attribution
                        if dataset_contributions:
                            rows_per_dataset = row_count // len(dataset_contributions)
                            for contribution in dataset_contributions:
                                contribution.csv_billing_rows += rows_per_dataset
                                contribution.file_count += 1

                    except Exception:
                        continue

                    progress.advance(task)

            return total_csv_rows, total_files_processed, dataset_contributions

        except Exception:
            return 0, 0, []

    def _get_csv_gz_files(self, folder_path: str) -> List[str]:
        """Get all CSV.gz files in a folder."""
        try:
            files = self.s3_service.list_files(folder_path)
            csv_files = [f for f in files if f.endswith('.csv.gz')]
            return csv_files
        except Exception:
            return []

    def _count_csv_gz_rows(self, s3_key: str) -> int:
        """
        Count rows in a CSV.gz file.

        This is a performance-critical operation that can be slow for large files.
        """
        try:
            # Download and count rows
            response = self.s3_service.s3_client.get_object(Bucket=self.s3_service.bucket, Key=s3_key)

            # Stream the gzipped content and count rows
            row_count = 0
            with gzip.GzipFile(fileobj=response['Body']) as gz_file:
                # Use csv.reader to properly handle CSV format
                csv_reader = csv.reader(gz_file.read().decode('utf-8').splitlines())

                # Skip header if present
                first_row = True
                for row in csv_reader:
                    if first_row:
                        # Skip header row
                        first_row = False
                        continue

                    if row:  # Skip empty rows
                        row_count += 1

            return row_count

        except Exception:
            return 0

    def list_all_organizations_with_drops(self) -> List[str]:
        """List all organizations that have billing drops."""
        try:
            # List all organization folders in piedpiper-custom-billing
            billing_path = "piedpiper-custom-billing/"
            org_folders = self.s3_service.list_folders(billing_path)

            organizations = []
            for folder in org_folders:
                folder_name = folder.rstrip('/').split('/')[-1]
                if folder_name:  # Any non-empty folder name is an organization
                    organizations.append(folder_name)

            return sorted(set(organizations))  # Remove duplicates and sort

        except Exception:
            return []

    def list_organization_providers(self, organization: str) -> List[str]:
        """List all providers for an organization (lightweight operation)."""
        org_path = f"piedpiper-custom-billing/{organization}/"
        providers = []

        try:
            # List provider folders
            provider_folders = self.s3_service.list_folders(org_path)

            for provider_folder in provider_folders:
                provider = provider_folder.rstrip('/').split('/')[-1]
                if provider:
                    providers.append(provider.upper())  # Normalize to uppercase

            return sorted(set(providers))  # Remove duplicates and sort

        except Exception:
            return []