"""GDPR data export service (Right to Access / Right to Data Portability).

⚠️ AUTO-GENERATED BY PRISM — DO NOT EDIT
This file is regenerated on every `prisme generate`.
"""

from __future__ import annotations

import csv
import io
import json
import logging
from datetime import datetime, timezone
from typing import Any

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from {{ package_name }}.privacy.audit_service import AuditService
{% for model in personal_data_models %}
from {{ package_name }}.models.{{ model.snake_name }} import {{ model.name }}
{% endfor %}

logger = logging.getLogger(__name__)


class DataExportService:
    """Handles GDPR data subject access requests (Article 15) and
    data portability (Article 20).

    Exports all personal data associated with a subject identifier
    in the requested format.
    """

    SUPPORTED_FORMATS = {{ supported_formats }}

    def __init__(self, session: AsyncSession) -> None:
        self.session = session
        self.audit = AuditService(session)

    async def export_subject_data(
        self,
        *,
        subject_id: str,
        subject_field: str = "id",
        format: str = "json",
        requesting_user_id: str | None = None,
        ip_address: str | None = None,
    ) -> dict[str, Any]:
        """Export all personal data for a data subject.

        Args:
            subject_id: The identifier of the data subject.
            subject_field: The field name to match (default: 'id').
            format: Export format ('json' or 'csv').
            requesting_user_id: User who initiated the request (for audit).
            ip_address: Client IP address (for audit).

        Returns:
            Dictionary with 'format', 'exported_at', and 'data' keys.
        """
        if format not in self.SUPPORTED_FORMATS:
            msg = f"Unsupported format '{format}'. Use one of: {self.SUPPORTED_FORMATS}"
            raise ValueError(msg)

        data: dict[str, Any] = {}
{% for model in personal_data_models %}

        # Export {{ model.name }} records
        {{ model.snake_name }}_stmt = select({{ model.name }}).where(
            getattr({{ model.name }}, subject_field) == subject_id
        )
        {{ model.snake_name }}_result = await self.session.execute({{ model.snake_name }}_stmt)
        {{ model.snake_name }}_records = {{ model.snake_name }}_result.scalars().all()
        data["{{ model.snake_name }}"] = [
            self._model_to_dict(record, {{ model.pii_fields }})
            for record in {{ model.snake_name }}_records
        ]
{% endfor %}

        # Audit the export
        await self.audit.log(
            operation="export",
            model_name="*",
            record_id=subject_id,
            user_id=requesting_user_id,
            ip_address=ip_address,
            details=f"Data subject export in {format} format",
        )

        export = {
            "format": format,
            "exported_at": datetime.now(timezone.utc).isoformat(),
            "subject_id": subject_id,
            "data": data,
        }

        if format == "csv":
            export["csv_files"] = self._to_csv(data)

        return export

    def _model_to_dict(
        self, record: Any, pii_fields: list[str] | None = None
    ) -> dict[str, Any]:
        """Convert a SQLAlchemy model instance to a dictionary.

        Args:
            record: The SQLAlchemy model instance.
            pii_fields: Optional list of PII field names to include.
                         If None, all columns are included.
        """
        result = {}
        for column in record.__table__.columns:
            if pii_fields is None or column.name in pii_fields:
                value = getattr(record, column.name)
                if isinstance(value, datetime):
                    value = value.isoformat()
                result[column.name] = value
        return result

    def _to_csv(self, data: dict[str, list[dict[str, Any]]]) -> dict[str, str]:
        """Convert export data to CSV strings keyed by model name."""
        csv_files: dict[str, str] = {}
        for model_name, records in data.items():
            if not records:
                continue
            output = io.StringIO()
            writer = csv.DictWriter(output, fieldnames=records[0].keys())
            writer.writeheader()
            writer.writerows(records)
            csv_files[model_name] = output.getvalue()
        return csv_files
