"""
File sequence coordinator for integrated append workflow.

Ensures copy and upload phases use coordinated sequence numbers to prevent
file overwrites and maintain proper data-export-NNNN.csv.gz naming.
"""

from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass

from .manifest_reader import S3FileInfo


@dataclass
class SequenceState:
    """Current sequence state for a provider/month combination."""
    provider: str
    billing_period: str
    source_file_count: int
    starting_sequence: int
    next_available_sequence: int
    copied_files: int
    uploaded_files: int


class FileSequenceCoordinator:
    """
    Service for coordinating file sequence numbers across copy and upload phases.
    
    Single responsibility: Ensure both copy and upload operations use proper
    sequential file numbers without conflicts or overwrites.
    """
    
    def __init__(self):
        """Initialize sequence coordinator with empty state."""
        self.sequence_states: Dict[str, SequenceState] = {}  # Key: "{provider}:{billing_period}"
    
    def initialize_sequence_state(
        self, 
        provider: str, 
        billing_period: str, 
        source_files: List[S3FileInfo]
    ) -> SequenceState:
        """
        Initialize sequence state for a provider/month combination.
        
        Analyzes source files to determine starting sequence numbers for
        both copy and upload operations.
        
        Args:
            provider: Provider name (AWS, GCP, Azure)
            billing_period: Billing period (YYYYMMDD-YYYYMMDD)
            source_files: Existing files from source drop
            
        Returns:
            SequenceState for tracking file numbering
        """
        state_key = f"{provider}:{billing_period}"
        
        # Calculate starting sequence from source files
        starting_sequence = self._calculate_starting_sequence(source_files)
        
        # Initialize state
        state = SequenceState(
            provider=provider,
            billing_period=billing_period,
            source_file_count=len(source_files),
            starting_sequence=starting_sequence,
            next_available_sequence=starting_sequence,
            copied_files=0,
            uploaded_files=0
        )
        
        self.sequence_states[state_key] = state
        
        print(f"   📊 Sequence state for {provider}/{billing_period}: start={starting_sequence}, source_files={len(source_files)}")
        
        return state
    
    def get_copy_sequence_range(
        self, 
        provider: str, 
        billing_period: str, 
        file_count: int
    ) -> Tuple[int, int]:
        """
        Get sequence number range for copy operations.
        
        Copy operations use the original sequence numbers from source files
        (1, 2, 3, ...) to maintain consistency with source drop.
        
        Args:
            provider: Provider name
            billing_period: Billing period
            file_count: Number of files being copied
            
        Returns:
            Tuple of (start_sequence, end_sequence) for copy operations
        """
        state_key = f"{provider}:{billing_period}"
        state = self.sequence_states.get(state_key)
        
        if not state:
            raise ValueError(f"Sequence state not initialized for {provider}:{billing_period}")
        
        if file_count != state.source_file_count:
            raise ValueError(f"Copy file count {file_count} doesn't match source file count {state.source_file_count}")
        
        # Copy operations preserve original sequence numbers (1 to source_file_count)
        start_sequence = 1
        end_sequence = state.source_file_count
        
        # Update state
        state.copied_files = file_count
        
        return start_sequence, end_sequence
    
    def get_upload_sequence_range(
        self, 
        provider: str, 
        billing_period: str, 
        file_count: int
    ) -> Tuple[int, int]:
        """
        Get sequence number range for upload operations.
        
        Upload operations continue from where source files left off,
        ensuring no conflicts with copied files.
        
        Args:
            provider: Provider name
            billing_period: Billing period
            file_count: Number of files being uploaded
            
        Returns:
            Tuple of (start_sequence, end_sequence) for upload operations
        """
        state_key = f"{provider}:{billing_period}"
        state = self.sequence_states.get(state_key)
        
        if not state:
            raise ValueError(f"Sequence state not initialized for {provider}:{billing_period}")
        
        # Upload operations start after all source files
        start_sequence = state.starting_sequence
        end_sequence = start_sequence + file_count - 1
        
        # Update state
        state.uploaded_files = file_count
        state.next_available_sequence = end_sequence + 1
        
        return start_sequence, end_sequence
    
    def validate_sequence_coordination(self, provider: str, billing_period: str) -> Dict[str, any]:
        """
        Validate that sequence coordination is working correctly.
        
        Checks for potential conflicts, gaps, or overlaps in sequence numbering
        between copy and upload operations.
        
        Args:
            provider: Provider name  
            billing_period: Billing period
            
        Returns:
            Validation result dict with 'valid' boolean and 'issues' list
        """
        state_key = f"{provider}:{billing_period}"
        state = self.sequence_states.get(state_key)
        
        if not state:
            return {
                'valid': False,
                'issues': [f"No sequence state found for {provider}:{billing_period}"]
            }
        
        issues = []
        
        # Check for sequence gaps or overlaps
        copy_sequences = list(range(1, state.source_file_count + 1)) if state.copied_files > 0 else []
        upload_sequences = list(range(state.starting_sequence, state.starting_sequence + state.uploaded_files)) if state.uploaded_files > 0 else []
        
        # Check for overlaps between copy and upload sequences
        copy_set = set(copy_sequences)
        upload_set = set(upload_sequences)
        overlaps = copy_set.intersection(upload_set)
        
        if overlaps:
            issues.append(f"Sequence number overlaps detected: {sorted(overlaps)}")
        
        # Check for unexpected gaps
        if copy_sequences and upload_sequences:
            expected_upload_start = max(copy_sequences) + 1
            actual_upload_start = min(upload_sequences) if upload_sequences else 0
            
            if actual_upload_start != expected_upload_start:
                issues.append(f"Upload sequence gap: expected start {expected_upload_start}, actual start {actual_upload_start}")
        
        return {
            'valid': len(issues) == 0,
            'issues': issues,
            'copy_sequences': copy_sequences,
            'upload_sequences': upload_sequences,
            'total_files': len(copy_sequences) + len(upload_sequences)
        }
    
    def _calculate_starting_sequence(self, source_files: List[S3FileInfo]) -> int:
        """
        Calculate starting sequence number for upload operations.
        
        Analyzes existing data-export-NNNN.csv.gz files to find the next
        available sequence number for new uploads.
        
        Args:
            source_files: List of existing files from source drop
            
        Returns:
            Next available sequence number for uploads
        """
        if not source_files:
            return 1
        
        max_sequence = 0
        for file_info in source_files:
            # Extract sequence number from filename: data-export-NNNN.csv.gz
            if file_info.filename.startswith('data-export-'):
                try:
                    # Extract the NNNN part
                    sequence_part = file_info.filename[12:16]  # data-export-NNNN
                    sequence_num = int(sequence_part)
                    max_sequence = max(max_sequence, sequence_num)
                except (ValueError, IndexError):
                    # Skip files with invalid sequence numbers
                    continue
        
        return max_sequence + 1
    
    def get_sequence_state(self, provider: str, billing_period: str) -> Optional[SequenceState]:
        """
        Retrieve current sequence state for a provider/month combination.
        
        Args:
            provider: Provider name
            billing_period: Billing period
            
        Returns:
            SequenceState if found, None otherwise
        """
        state_key = f"{provider}:{billing_period}"
        return self.sequence_states.get(state_key)
    
    def generate_copy_filenames(
        self, 
        provider: str, 
        billing_period: str, 
        source_files: List[S3FileInfo]
    ) -> List[str]:
        """
        Generate target filenames for copy operations.
        
        Copy operations preserve original filenames to maintain consistency
        with the source drop structure.
        
        Args:
            provider: Provider name
            billing_period: Billing period
            source_files: Source files being copied
            
        Returns:
            List of target filenames for copied files
        """
        # Copy operations preserve original filenames
        return [file_info.filename for file_info in source_files]
    
    def generate_upload_filenames(
        self, 
        provider: str, 
        billing_period: str, 
        file_count: int
    ) -> List[str]:
        """
        Generate filenames for upload operations.
        
        Upload operations use sequential numbering starting after
        the highest sequence number from copied files.
        
        Args:
            provider: Provider name
            billing_period: Billing period
            file_count: Number of files being uploaded
            
        Returns:
            List of filenames for uploaded files
        """
        start_sequence, end_sequence = self.get_upload_sequence_range(
            provider, billing_period, file_count
        )
        
        filenames = []
        for sequence in range(start_sequence, end_sequence + 1):
            filename = f"data-export-{sequence:04d}.csv.gz"
            filenames.append(filename)
        
        return filenames
    
    def reset_state(self) -> None:
        """Reset all sequence states for fresh coordination."""
        self.sequence_states.clear()