"""
Checksum Calculator Module
Provides SHA256 hash calculation and verification with caching support.
"""

import hashlib
import os
from typing import Optional, Dict
from pathlib import Path


class ChecksumCalculator:
    """
    Handles SHA256 checksum calculation and verification.
    Implements caching based on file modification time for performance.
    """
    
    CHUNK_SIZE = 65536  # 64KB chunks for reading large files
    
    def __init__(self):
        """Initialize the checksum calculator with cache."""
        self._cache: Dict[str, tuple] = {}  # {file_path: (mtime, sha256)}
    
    def calculate_sha256(self, file_path: str, use_cache: bool = True) -> Optional[str]:
        """
        Calculate SHA256 hash of a file.
        
        Args:
            file_path: Path to the file
            use_cache: Whether to use cached hash if available
            
        Returns:
            SHA256 hash as hexadecimal string, or None if file doesn't exist
        """
        if not os.path.exists(file_path):
            return None
        
        try:
            # Get file modification time
            mtime = os.path.getmtime(file_path)
            
            # Check cache
            if use_cache and file_path in self._cache:
                cached_mtime, cached_hash = self._cache[file_path]
                if cached_mtime == mtime:
                    return cached_hash
            
            # Calculate hash
            sha256_hash = hashlib.sha256()
            
            with open(file_path, 'rb') as f:
                while True:
                    chunk = f.read(self.CHUNK_SIZE)
                    if not chunk:
                        break
                    sha256_hash.update(chunk)
            
            result = sha256_hash.hexdigest()
            
            # Update cache
            if use_cache:
                self._cache[file_path] = (mtime, result)
            
            return result
            
        except PermissionError:
            # Can't read file due to permissions
            return None
        except Exception as e:
            # Other errors
            return None
    
    def verify_checksum(self, file_path: str, expected_hash: str, 
                       use_cache: bool = True) -> bool:
        """
        Verify if a file's SHA256 hash matches the expected value.
        
        Args:
            file_path: Path to the file
            expected_hash: Expected SHA256 hash
            use_cache: Whether to use cached hash if available
            
        Returns:
            True if hash matches, False otherwise
        """
        actual_hash = self.calculate_sha256(file_path, use_cache)
        
        if actual_hash is None:
            return False
        
        return actual_hash.lower() == expected_hash.lower()
    
    def calculate_directory_hashes(self, directory: str, 
                                   recursive: bool = True) -> Dict[str, str]:
        """
        Calculate SHA256 hashes for all files in a directory.
        
        Args:
            directory: Path to the directory
            recursive: Whether to process subdirectories
            
        Returns:
            Dictionary mapping file paths to SHA256 hashes
        """
        hashes = {}
        
        if not os.path.exists(directory):
            return hashes
        
        if recursive:
            for root, dirs, files in os.walk(directory):
                for filename in files:
                    file_path = os.path.join(root, filename)
                    file_hash = self.calculate_sha256(file_path, use_cache=False)
                    if file_hash:
                        # Store relative path
                        rel_path = os.path.relpath(file_path, directory)
                        hashes[rel_path] = file_hash
        else:
            for item in os.listdir(directory):
                file_path = os.path.join(directory, item)
                if os.path.isfile(file_path):
                    file_hash = self.calculate_sha256(file_path, use_cache=False)
                    if file_hash:
                        hashes[item] = file_hash
        
        return hashes
    
    def clear_cache(self):
        """Clear the checksum cache."""
        self._cache.clear()
    
    def get_cache_size(self) -> int:
        """Get the number of cached checksums."""
        return len(self._cache)
    
    @staticmethod
    def calculate_string_sha256(content: str) -> str:
        """
        Calculate SHA256 hash of a string.
        
        Args:
            content: String content to hash
            
        Returns:
            SHA256 hash as hexadecimal string
        """
        return hashlib.sha256(content.encode('utf-8')).hexdigest()
    
    @staticmethod
    def compare_files(file1: str, file2: str) -> bool:
        """
        Compare two files using SHA256 hash.
        
        Args:
            file1: Path to first file
            file2: Path to second file
            
        Returns:
            True if files have identical content, False otherwise
        """
        calc = ChecksumCalculator()
        hash1 = calc.calculate_sha256(file1, use_cache=False)
        hash2 = calc.calculate_sha256(file2, use_cache=False)
        
        if hash1 is None or hash2 is None:
            return False
        
        return hash1 == hash2
