"""Main anonymization module."""
from typing import Optional, List, Dict, Any
from faker import Faker
from google.cloud import dlp_v2, firestore
import google.api_core.exceptions as google_exceptions
from .config import (
    DEFAULT_INFO_TYPES,
    REQUIRED_SERVICES,
    DEFAULT_COLLECTION_NAME,
    DEFAULT_LOCATION,
)
from .exceptions import AnonymizationError, DeAnonymizationError
from .utils import check_required_services


class ReversibleAnonymizer:
    def __init__(
            self,
            project: str,
            info_types: Optional[List[Dict[str, str]]] = None,
            collection_name: str = DEFAULT_COLLECTION_NAME,
            location: str = DEFAULT_LOCATION,
            check_services: bool = True
    ):
        """
        Initialize the ReversibleAnonymizer.

        Args:
            project: Google Cloud project ID
            info_types: List of info types to detect (default: DEFAULT_INFO_TYPES)
            collection_name: Firestore collection name for storing mappings
            location: Google Cloud location
            check_services: Whether to check if required services are enabled
        """
        self.project = project
        self.info_types = info_types or DEFAULT_INFO_TYPES
        self.collection_name = collection_name
        self.location = location

        if check_services:
            check_required_services(project, REQUIRED_SERVICES)

        self.dlp = dlp_v2.DlpServiceClient()
        self.fake = Faker()
        self.db = firestore.Client()

    def anonymize(self, text_to_deidentify: str) -> str:
        """
        Anonymize text by replacing sensitive information with fake data.

        Args:
            text_to_deidentify: Text to anonymize

        Returns:
            Anonymized text

        Raises:
            AnonymizationError: If anonymization fails
        """
        try:
            parent = f"projects/{self.project}/locations/{self.location}"
            inspect_config = dlp_v2.InspectConfig(
                info_types=self.info_types,
                include_quote=True
            )
            item = dlp_v2.ContentItem(value=text_to_deidentify)

            response = self.dlp.inspect_content(
                request={"parent": parent,
                         "inspect_config": inspect_config,
                         "item": item}
            )

            for finding in response.result.findings:
                docs = (
                    self.db.collection(self.collection_name)
                    .where("original_data", "==", finding.quote)
                    .stream()
                )
                docs = list(docs)

                if docs:
                    fake_data = docs[0].id
                else:
                    fake_data = self._generate_fake_data(finding.info_type.name)
                    doc_ref = self.db.collection(
                        self.collection_name
                    ).document(fake_data)
                    doc_ref.set({"original_data": finding.quote})

                text_to_deidentify = text_to_deidentify.replace(
                    finding.quote, fake_data
                )

            return text_to_deidentify

        except Exception as e:
            raise AnonymizationError(f"Anonymization failed: {str(e)}")

    def deanonymize(self, text: str) -> str:
        """
        De-anonymize text by replacing fake data with original data.

        Args:
            text: Text to de-anonymize

        Returns:
            De-anonymized text

        Raises:
            DeAnonymizationError: If de-anonymization fails
        """
        try:
            docs = self.db.collection(self.collection_name).stream()
            for doc in docs:
                text = text.replace(doc.id, doc.to_dict()["original_data"])
            return text

        except Exception as e:
            raise DeAnonymizationError(f"De-anonymization failed: {str(e)}")

    def _generate_fake_data(self, info_type: str) -> str:
        """Generate fake data based on info type."""
        fake_data_mapping = {
            "PERSON_NAME": self.fake.name,
            "FIRST_NAME": self.fake.first_name,
            "LAST_NAME": self.fake.last_name,
            "PHONE_NUMBER": self.fake.phone_number,
        }

        generator = fake_data_mapping.get(info_type, self.fake.word)
        return generator()