# src/fmatch/saas/services/survey_results_service.py
"""
Production survey results service with caching, pagination, and action bridging.
"""

from typing import Dict, Any, List, Optional
from datetime import datetime
from sqlalchemy import select, func, and_
from sqlalchemy.ext.asyncio import AsyncSession
import json
import logging
from redis import asyncio as aioredis

from ..models import ActionBatch, ActionStatus
from ..model_defs import SurveyResult, SurveyFinding

logger = logging.getLogger(__name__)


class SurveyResultsService:
    """Handles survey results, storage, and action conversion."""

    def __init__(self, db: AsyncSession, redis_client: Optional[aioredis.Redis] = None):
        self.db = db
        self.redis = redis_client
        self.cache_ttl = 3600  # 1 hour cache

    async def store_results(
        self,
        survey_id: str,
        results: Dict[str, Any],
        integration_id: str,
        account_id: str,
    ) -> SurveyResult:
        """Store survey results with findings."""

        # Create main survey result
        survey = SurveyResult(
            id=survey_id,
            account_id=account_id,
            integration_id=integration_id,
            completed_at=datetime.utcnow(),
            duration_seconds=results.get("duration_seconds", 0),
            status="complete",
            records_analyzed=results["summary"]["records_analyzed"],
            duplicates_found=results["summary"]["duplicates_found"],
            enrichment_opportunities=results["summary"]["enrichment_opportunities"],
            data_quality_score=results["summary"]["data_quality_score"],
            grade=results["summary"]["grade"],
            raw_results=results,
            field_stats=self._extract_field_stats(results),
            proposed_policies=self._extract_policies(results),
            insights=self._generate_insights(results),
        )

        self.db.add(survey)

        # Create findings from duplicates
        findings_created = 0
        for obj_name, obj_data in results.get("objects", {}).items():
            if "duplicates" in obj_data:
                for dup in obj_data["duplicates"][:500]:  # Cap for performance
                    finding = SurveyFinding(
                        survey_id=survey_id,
                        object_type=obj_name,
                        finding_type="duplicate",
                        record_ids=[r["Id"] for r in dup["records"]],
                        confidence_score=dup["score"],
                        confidence_tier=dup["confidence_tier"],
                        match_reasons=dup.get("contributions", {}),
                        explanation=dup.get("explanation", "Similar records detected"),
                    )
                    self.db.add(finding)
                    findings_created += 1

            # Also create enrichment findings
            if "enrichment_opportunities" in obj_data:
                opps = obj_data["enrichment_opportunities"]
                if opps.get("missing_emails", 0) > 0:
                    finding = SurveyFinding(
                        survey_id=survey_id,
                        object_type=obj_name,
                        finding_type="enrichment",
                        field_name="Email",
                        issue_type="missing",
                        suggested_provider="Multiple available",
                    )
                    self.db.add(finding)

        await self.db.commit()
        await self.db.refresh(survey)

        # Cache results in Redis if available
        if self.redis:
            await self.redis.setex(
                f"survey:results:{survey_id}", self.cache_ttl, json.dumps(results)
            )

        logger.info(f"Stored survey {survey_id} with {findings_created} findings")
        return survey

    async def get_survey_with_findings(
        self,
        survey_id: str,
        page: int = 1,
        page_size: int = 50,
        min_confidence: Optional[float] = None,
        finding_type: Optional[str] = None,
        tier: Optional[str] = None,
        object_type: Optional[str] = None,
        status: Optional[str] = None,
    ) -> Dict[str, Any]:
        """Get survey with paginated findings."""

        # Try cache first
        if self.redis:
            cached = await self.redis.get(f"survey:findings:{survey_id}:{page}")
            if cached:
                return json.loads(cached)

        # Get survey
        result = await self.db.execute(
            select(SurveyResult).where(SurveyResult.id == survey_id)
        )
        survey = result.scalar_one_or_none()

        if not survey:
            raise ValueError(f"Survey {survey_id} not found")

        # Build findings query
        query = select(SurveyFinding).where(SurveyFinding.survey_id == survey_id)

        if min_confidence is not None:
            query = query.where(SurveyFinding.confidence_score >= min_confidence)

        if finding_type:
            query = query.where(SurveyFinding.finding_type == finding_type)

        # CRITICAL: Add missing filters for tier, object_type, and status
        if tier:
            query = query.where(SurveyFinding.confidence_tier == tier)
        if object_type:
            query = query.where(SurveyFinding.object_type == object_type)
        if status and status != "all":
            query = query.where(SurveyFinding.status == status)

        # Get total count
        count_result = await self.db.execute(
            select(func.count())
            .select_from(SurveyFinding)
            .where(SurveyFinding.survey_id == survey_id)
        )
        total_count = count_result.scalar()

        # Get paginated findings
        query = query.order_by(SurveyFinding.confidence_score.desc())
        query = query.offset((page - 1) * page_size).limit(page_size)

        findings_result = await self.db.execute(query)
        findings = findings_result.scalars().all()

        response = {
            "survey": {
                "id": survey.id,
                "status": survey.status,
                "completed_at": survey.completed_at.isoformat()
                if survey.completed_at
                else None,
                "summary": {
                    "records_analyzed": survey.records_analyzed,
                    "duplicates_found": survey.duplicates_found,
                    "data_quality_score": survey.data_quality_score,
                    "grade": survey.grade,
                },
                "insights": survey.insights,
            },
            "findings": [
                {
                    "id": f.id,
                    "type": f.finding_type,
                    "object_type": f.object_type,
                    "confidence_score": f.confidence_score,
                    "confidence_tier": f.confidence_tier,
                    "explanation": f.explanation,
                    "record_ids": f.record_ids,
                    "status": f.status,
                }
                for f in findings
            ],
            "pagination": {
                "page": page,
                "page_size": page_size,
                "total_count": total_count,
                "total_pages": (total_count + page_size - 1) // page_size,
            },
        }

        # Cache response
        if self.redis:
            await self.redis.setex(
                f"survey:findings:{survey_id}:{page}",
                300,  # 5 min cache for findings
                json.dumps(response),
            )

        return response

    async def convert_to_action_batch(
        self, survey_id: str, finding_ids: List[str], user_id: str
    ) -> ActionBatch:
        """Convert survey findings to actionable batch."""

        # Get findings
        result = await self.db.execute(
            select(SurveyFinding).where(
                and_(
                    SurveyFinding.survey_id == survey_id,
                    SurveyFinding.id.in_(finding_ids),
                )
            )
        )
        findings = result.scalars().all()

        if not findings:
            raise ValueError("No findings found")

        # Group by action type
        merge_items = []
        enrichment_items = []

        for finding in findings:
            if finding.finding_type == "duplicate":
                merge_items.append(
                    {
                        "finding_id": finding.id,
                        "object_type": finding.object_type,
                        "record_ids": finding.record_ids,
                        "confidence": finding.confidence_score,
                        "explanation": finding.explanation,
                    }
                )
            elif finding.finding_type == "enrichment":
                enrichment_items.append(
                    {
                        "finding_id": finding.id,
                        "object_type": finding.object_type,
                        "field": finding.field_name,
                        "issue": finding.issue_type,
                        "suggested_value": finding.suggested_value,
                    }
                )

        # Create action batch
        batch = ActionBatch(
            investigation_id=survey_id,  # Link to survey
            created_by_user_id=user_id,
            items=merge_items + enrichment_items,
            preview={
                "merge_count": len(merge_items),
                "enrichment_count": len(enrichment_items),
                "total_items": len(findings),
            },
            status=ActionStatus.PREPARED,
        )

        self.db.add(batch)
        await self.db.commit()
        await self.db.refresh(batch)

        logger.info(f"Created action batch {batch.id} from {len(findings)} findings")
        return batch

    async def update_finding_status(
        self, finding_id: str, status: str, user_id: str, notes: Optional[str] = None
    ):
        """Update finding status after action."""
        result = await self.db.execute(
            select(SurveyFinding).where(SurveyFinding.id == finding_id)
        )
        finding = result.scalar_one_or_none()

        if finding:
            finding.status = status
            finding.actioned_at = datetime.utcnow()
            finding.actioned_by = user_id
            finding.action_notes = notes
            await self.db.commit()

    def _extract_field_stats(self, results: Dict) -> Dict:
        """Extract field statistics from results."""
        stats = {}
        for obj_name, obj_data in results.get("objects", {}).items():
            if "field_stats" in obj_data:
                stats[obj_name] = obj_data["field_stats"]
        return stats

    def _extract_policies(self, results: Dict) -> Dict:
        """Extract proposed policies from results."""
        policies = {}
        for obj_name, obj_data in results.get("objects", {}).items():
            if "proposed_policy" in obj_data:
                policies[obj_name] = obj_data["proposed_policy"]
        return policies

    def _generate_insights(self, results: Dict) -> List[Dict]:
        """Generate actionable insights from results."""
        insights = []
        summary = results.get("summary", {})

        # Duplicate insight
        if summary.get("duplicates_found", 0) > 10:
            insights.append(
                {
                    "type": "duplicates",
                    "priority": "HIGH",
                    "title": f"Found {summary['duplicates_found']} duplicate records",
                    "description": "Merging these duplicates will improve data accuracy and reporting",
                    "estimated_time": f"{summary['duplicates_found'] * 2} minutes",
                    "estimated_impact": "15-20% improvement in reporting accuracy",
                }
            )

        # Data quality insight
        if summary.get("data_quality_score", 100) < 70:
            insights.append(
                {
                    "type": "quality",
                    "priority": "MEDIUM",
                    "title": f"Data quality score: {summary['data_quality_score']}% (Grade: {summary['grade']})",
                    "description": "Improving data completeness will increase campaign effectiveness",
                    "estimated_time": "2-3 hours with enrichment",
                    "estimated_impact": "25% increase in contactability",
                }
            )

        # Enrichment insight
        if summary.get("enrichment_opportunities", 0) > 100:
            insights.append(
                {
                    "type": "enrichment",
                    "priority": "MEDIUM",
                    "title": f"{summary['enrichment_opportunities']} fields could be enriched",
                    "description": "Fill missing emails, phones, and company data automatically",
                    "estimated_time": "Instant with automation",
                    "estimated_impact": "30% more complete records",
                }
            )

        return insights
