"""BLCE Intake Questionnaire Generator.

Builds a structured questionnaire from E2E pipeline results,
auto-populating fields where data already exists.
"""
from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional

from .constants import IntakeSection
from .contracts import (
    E2EHandoff,
    ExtractedKPI,
    IntakeQuestion,
    IntakeQuestionnaire,
)

logger = logging.getLogger(__name__)


class IntakeQuestionnaireGenerator:
    """Generate and process consultant intake questionnaires."""

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def generate(
        self,
        handoff: E2EHandoff,
        erp_type: str = "",
        client_id: str = "",
    ) -> IntakeQuestionnaire:
        """Build full Section A-G template, auto-populating from E2E data.

        Args:
            handoff: E2EHandoff with profiles/classifications/relationships.
            erp_type: Override for ERP type (auto-detected if empty).
            client_id: Client identifier.

        Returns:
            IntakeQuestionnaire with 7 sections.
        """
        sections = self._build_questionnaire_template()

        # Auto-populate from E2E data
        detected_erp = erp_type or self._auto_detect_erp(handoff)
        self._auto_populate_erp_info(sections, handoff, detected_erp)
        self._auto_populate_dimensions(sections, handoff)
        self._auto_populate_quality_issues(sections, handoff)

        return IntakeQuestionnaire(
            client_id=client_id,
            e2e_handoff_id=handoff.handoff_id,
            erp_type=detected_erp,
            sections=sections,
        )

    def process_completed(
        self,
        questionnaire: IntakeQuestionnaire,
    ) -> Dict[str, Any]:
        """Parse completed questionnaire responses into structured data.

        Returns:
            Dict with departments, business_questions, kpis, pain_points.
        """
        departments: List[str] = []
        business_questions: List[str] = []
        kpis: List[str] = []
        pain_points: List[str] = []

        for section_key, questions in questionnaire.sections.items():
            for q in questions:
                if not q.response:
                    continue

                if section_key == IntakeSection.ORGANIZATION.value:
                    if "department" in q.question_id:
                        departments.extend(
                            d.strip() for d in q.response.split(",") if d.strip()
                        )

                elif section_key == IntakeSection.BUSINESS_QUESTIONS.value:
                    business_questions.append(q.response)

                elif section_key == IntakeSection.CURRENT_STATE.value:
                    if "pain" in q.question_id or "challenge" in q.question_id:
                        pain_points.append(q.response)
                    if "kpi" in q.question_id:
                        kpis.extend(
                            k.strip() for k in q.response.split(",") if k.strip()
                        )

        questionnaire.departments = departments
        questionnaire.business_questions = business_questions
        questionnaire.kpis = kpis
        questionnaire.pain_points = pain_points

        return {
            "departments": departments,
            "business_questions": business_questions,
            "kpis": kpis,
            "pain_points": pain_points,
            "sections_with_responses": sum(
                1
                for qs in questionnaire.sections.values()
                if any(q.response for q in qs)
            ),
        }

    # ------------------------------------------------------------------
    # Template builder
    # ------------------------------------------------------------------

    def _build_questionnaire_template(self) -> Dict[str, List[IntakeQuestion]]:
        """Return all 7 sections with question text."""
        return {
            IntakeSection.ORGANIZATION.value: [
                IntakeQuestion(
                    section=IntakeSection.ORGANIZATION,
                    question_id="org_name",
                    question_text="What is the organization name?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.ORGANIZATION,
                    question_id="org_industry",
                    question_text="What industry sector does the organization operate in?",
                    question_type="select",
                    options=["Oil & Gas", "Mining", "Manufacturing", "Financial Services", "Healthcare", "Other"],
                ),
                IntakeQuestion(
                    section=IntakeSection.ORGANIZATION,
                    question_id="org_departments",
                    question_text="Which departments will consume the data warehouse? (comma-separated)",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.ORGANIZATION,
                    question_id="org_user_count",
                    question_text="Estimated number of report consumers?",
                    question_type="number",
                ),
            ],
            IntakeSection.CURRENT_STATE.value: [
                IntakeQuestion(
                    section=IntakeSection.CURRENT_STATE,
                    question_id="cs_erp_type",
                    question_text="What ERP system(s) are in use?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.CURRENT_STATE,
                    question_id="cs_table_count",
                    question_text="Approximate number of source tables?",
                    question_type="number",
                ),
                IntakeQuestion(
                    section=IntakeSection.CURRENT_STATE,
                    question_id="cs_reporting_tools",
                    question_text="Current reporting/BI tools in use?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.CURRENT_STATE,
                    question_id="cs_pain_points",
                    question_text="Top data challenges or pain points?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.CURRENT_STATE,
                    question_id="cs_current_kpis",
                    question_text="Key KPIs currently tracked (comma-separated)?",
                    question_type="text",
                ),
            ],
            IntakeSection.BUSINESS_QUESTIONS.value: [
                IntakeQuestion(
                    section=IntakeSection.BUSINESS_QUESTIONS,
                    question_id="bq_top_questions",
                    question_text="What are the top 5 business questions the DW should answer?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.BUSINESS_QUESTIONS,
                    question_id="bq_kpi_definitions",
                    question_text="List KPIs with their business definitions (name: formula).",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.BUSINESS_QUESTIONS,
                    question_id="bq_drill_paths",
                    question_text="What drill-down paths are needed? (e.g., Company > Area > Well)",
                    question_type="text",
                ),
            ],
            IntakeSection.DIMENSIONS.value: [
                IntakeQuestion(
                    section=IntakeSection.DIMENSIONS,
                    question_id="dim_confirmed",
                    question_text="Which of these detected dimensions are confirmed? (comma-separated)",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.DIMENSIONS,
                    question_id="dim_additional",
                    question_text="Any additional dimensions not detected?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.DIMENSIONS,
                    question_id="dim_scd_tables",
                    question_text="Which dimensions require SCD Type 2 (slowly changing)? (comma-separated)",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.DIMENSIONS,
                    question_id="dim_hierarchies",
                    question_text="Key hierarchies needed (e.g., Account > Sub-Account > Detail)?",
                    question_type="text",
                ),
            ],
            IntakeSection.DATA_QUALITY.value: [
                IntakeQuestion(
                    section=IntakeSection.DATA_QUALITY,
                    question_id="dq_known_issues",
                    question_text="Known data quality issues?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.DATA_QUALITY,
                    question_id="dq_null_tolerance",
                    question_text="Acceptable null rate for key fields? (%)",
                    question_type="number",
                ),
                IntakeQuestion(
                    section=IntakeSection.DATA_QUALITY,
                    question_id="dq_freshness",
                    question_text="Required data freshness (real-time, daily, weekly)?",
                    question_type="select",
                    options=["Real-time", "Hourly", "Daily", "Weekly", "Monthly"],
                ),
            ],
            IntakeSection.SECURITY.value: [
                IntakeQuestion(
                    section=IntakeSection.SECURITY,
                    question_id="sec_row_level",
                    question_text="Is row-level security required?",
                    question_type="boolean",
                ),
                IntakeQuestion(
                    section=IntakeSection.SECURITY,
                    question_id="sec_pii_columns",
                    question_text="Columns containing PII that must be masked?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.SECURITY,
                    question_id="sec_access_groups",
                    question_text="Access groups or roles required?",
                    question_type="text",
                ),
            ],
            IntakeSection.FUTURE_STATE.value: [
                IntakeQuestion(
                    section=IntakeSection.FUTURE_STATE,
                    question_id="fs_new_sources",
                    question_text="Planned new data sources in the next 12 months?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.FUTURE_STATE,
                    question_id="fs_growth_areas",
                    question_text="Business areas expected to grow or change?",
                    question_type="text",
                ),
                IntakeQuestion(
                    section=IntakeSection.FUTURE_STATE,
                    question_id="fs_bi_platform",
                    question_text="Target BI platform for the data warehouse?",
                    question_type="select",
                    options=["Sigma", "Power BI", "Tableau", "Looker", "Snowsight", "Other"],
                ),
            ],
        }

    # ------------------------------------------------------------------
    # Auto-population helpers
    # ------------------------------------------------------------------

    def _auto_detect_erp(self, handoff: E2EHandoff) -> str:
        """Use ERP registry to detect type from table names."""
        try:
            from src.data_modeling.erp_configs.registry import detect_erp_type

            table_names = [
                p.get("table_name", "") for p in handoff.table_profiles
            ]
            result = detect_erp_type(table_names)
            return result or "unknown"
        except Exception:
            return "unknown"

    def _auto_populate_erp_info(
        self,
        sections: Dict[str, List[IntakeQuestion]],
        handoff: E2EHandoff,
        erp_type: str,
    ) -> None:
        """Fill in current-state section from E2E data."""
        cs_questions = sections.get(IntakeSection.CURRENT_STATE.value, [])

        # Compute aggregate row counts and date ranges from profiles
        total_rows = 0
        min_date = ""
        max_date = ""
        for p in handoff.table_profiles:
            total_rows += p.get("row_count", 0) or 0
            for col in p.get("columns", []):
                if not isinstance(col, dict):
                    continue
                col_min = col.get("min", "")
                col_max = col.get("max", "")
                col_name = col.get("column_name", col.get("name", "")).upper()
                # Only look at date-like columns
                if not any(kw in col_name for kw in ("DATE", "_DT", "_TS", "TIMESTAMP")):
                    continue
                if col_min and isinstance(col_min, str) and (not min_date or col_min < min_date):
                    min_date = col_min
                if col_max and isinstance(col_max, str) and (not max_date or col_max > max_date):
                    max_date = col_max

        for q in cs_questions:
            if q.question_id == "cs_erp_type" and erp_type:
                q.auto_populated = True
                q.auto_value = erp_type
            elif q.question_id == "cs_table_count":
                q.auto_populated = True
                q.auto_value = str(len(handoff.table_profiles))

        # Add data volume question if not present, or populate existing
        volume_str = f"{total_rows:,} total rows across {len(handoff.table_profiles)} tables"
        date_str = f"{min_date} to {max_date}" if min_date and max_date else ""

        for q in cs_questions:
            if q.question_id == "cs_data_volume":
                q.auto_populated = True
                q.auto_value = volume_str
                break
        else:
            cs_questions.append(IntakeQuestion(
                section=IntakeSection.CURRENT_STATE,
                question_id="cs_data_volume",
                question_text="Total data volume across source tables?",
                question_type="text",
                auto_populated=True,
                auto_value=volume_str,
            ))

        if date_str:
            for q in cs_questions:
                if q.question_id == "cs_date_range":
                    q.auto_populated = True
                    q.auto_value = date_str
                    break
            else:
                cs_questions.append(IntakeQuestion(
                    section=IntakeSection.CURRENT_STATE,
                    question_id="cs_date_range",
                    question_text="Date range covered by source data?",
                    question_type="text",
                    auto_populated=True,
                    auto_value=date_str,
                ))

    def _auto_populate_dimensions(
        self,
        sections: Dict[str, List[IntakeQuestion]],
        handoff: E2EHandoff,
    ) -> None:
        """Fill in dimension section from E2E data.

        Fallback chain:
          1. DimensionDetector on relationships (FK-based)
          2. Enhanced table_analyses from TableAnalyzer (heuristic)
          3. DIM_ prefix matching
        """
        dims: List[str] = []

        # Strategy 1: FK-based relationships
        try:
            from src.data_modeling.dimension_detector import DimensionDetector

            if handoff.relationships:
                detector = DimensionDetector()
                classification = detector.classify_tables(handoff.relationships)
                dims = [t for t, c in classification.items() if c == "dimension"]
        except Exception:
            pass

        # Strategy 2: Enhanced table analyses from handoff run_summary
        if not dims:
            table_analyses = (handoff.run_summary or {}).get("table_analyses", {})
            if table_analyses:
                dims = [
                    t for t, a in table_analyses.items()
                    if a.get("role") == "reference"
                ]

        # Strategy 3: DIM_ prefix fallback
        if not dims:
            dims = [
                p.get("table_name", "")
                for p in handoff.table_profiles
                if p.get("table_name", "").upper().startswith("DIM_")
            ]

        if dims:
            dim_questions = sections.get(IntakeSection.DIMENSIONS.value, [])
            for q in dim_questions:
                if q.question_id == "dim_confirmed":
                    q.auto_populated = True
                    q.auto_value = ", ".join(sorted(dims))

        # Suggest key columns from natural_keys
        natural_keys = (handoff.run_summary or {}).get("natural_keys", {})
        if natural_keys:
            key_suggestions = []
            for tbl, keys in natural_keys.items():
                if keys:
                    key_suggestions.append(f"{tbl}: {', '.join(keys)}")
            if key_suggestions:
                dim_questions = sections.get(IntakeSection.DIMENSIONS.value, [])
                # Find or create a key columns question
                for q in dim_questions:
                    if q.question_id == "dim_natural_keys":
                        q.auto_populated = True
                        q.auto_value = "; ".join(key_suggestions[:15])
                        break
                else:
                    dim_questions.append(IntakeQuestion(
                        section=IntakeSection.DIMENSIONS,
                        question_id="dim_natural_keys",
                        question_text="Detected natural keys per table (verify)?",
                        question_type="text",
                        auto_populated=True,
                        auto_value="; ".join(key_suggestions[:15]),
                    ))

        # Suggest hierarchy patterns from domain_groups
        domain_groups = (handoff.run_summary or {}).get("domain_groups", {})
        if domain_groups:
            hierarchy_hints = []
            for domain, tables in domain_groups.items():
                if len(tables) >= 2:
                    hierarchy_hints.append(f"{domain}: {' > '.join(tables[:5])}")
            if hierarchy_hints:
                dim_questions = sections.get(IntakeSection.DIMENSIONS.value, [])
                for q in dim_questions:
                    if q.question_id == "dim_hierarchies":
                        if not q.auto_populated:
                            q.auto_populated = True
                            q.auto_value = "; ".join(hierarchy_hints[:10])
                        break

    def _auto_populate_quality_issues(
        self,
        sections: Dict[str, List[IntakeQuestion]],
        handoff: E2EHandoff,
    ) -> None:
        """Scan classifications for null rates, type mismatches, and low cardinality."""
        issues: List[str] = []
        type_mismatches: List[str] = []
        low_cardinality: List[str] = []

        for cls in handoff.classifications:
            col = cls.get("column_name", cls.get("column", "?"))
            tbl = cls.get("table_name", cls.get("table", "?"))

            # High null rate
            null_rate = cls.get("null_rate", 0) or cls.get("null_pct", 0)
            if isinstance(null_rate, (int, float)) and null_rate > 50:
                issues.append(f"{tbl}.{col}: {null_rate:.0f}% null")

            # Type mismatches: classification suggests unexpected type
            classification = cls.get("classification", "")
            data_type = cls.get("data_type", "").upper()
            if classification and data_type:
                cl = str(classification).lower()
                # Numeric column stored as string
                if cl in ("numeric", "currency", "percentage") and "VARCHAR" in data_type:
                    type_mismatches.append(f"{tbl}.{col}: {cl} stored as {data_type}")
                # Date column stored as string
                elif cl in ("date", "timestamp", "datetime") and "VARCHAR" in data_type:
                    type_mismatches.append(f"{tbl}.{col}: {cl} stored as {data_type}")

            # Low cardinality (potential dimension or boolean)
            distinct_count = cls.get("distinct_count", 0) or cls.get("n_distinct", 0)
            if isinstance(distinct_count, (int, float)) and 0 < distinct_count <= 5:
                low_cardinality.append(f"{tbl}.{col}: {int(distinct_count)} distinct values")

        if issues or type_mismatches:
            combined = issues[:5] + type_mismatches[:5]
            dq_questions = sections.get(IntakeSection.DATA_QUALITY.value, [])
            for q in dq_questions:
                if q.question_id == "dq_known_issues":
                    q.auto_populated = True
                    q.auto_value = "; ".join(combined[:10])

        # Add type mismatch detail as separate question
        if type_mismatches:
            dq_questions = sections.get(IntakeSection.DATA_QUALITY.value, [])
            for q in dq_questions:
                if q.question_id == "dq_type_mismatches":
                    q.auto_populated = True
                    q.auto_value = "; ".join(type_mismatches[:10])
                    break
            else:
                dq_questions.append(IntakeQuestion(
                    section=IntakeSection.DATA_QUALITY,
                    question_id="dq_type_mismatches",
                    question_text="Detected type mismatches (numeric/date stored as VARCHAR)?",
                    question_type="text",
                    auto_populated=True,
                    auto_value="; ".join(type_mismatches[:10]),
                ))

        # Add low cardinality flags
        if low_cardinality:
            dq_questions = sections.get(IntakeSection.DATA_QUALITY.value, [])
            for q in dq_questions:
                if q.question_id == "dq_low_cardinality":
                    q.auto_populated = True
                    q.auto_value = "; ".join(low_cardinality[:10])
                    break
            else:
                dq_questions.append(IntakeQuestion(
                    section=IntakeSection.DATA_QUALITY,
                    question_id="dq_low_cardinality",
                    question_text="Low-cardinality columns detected (possible boolean/flag)?",
                    question_type="text",
                    auto_populated=True,
                    auto_value="; ".join(low_cardinality[:10]),
                ))

        # Pre-fill freshness from max dates in profiles
        max_date = ""
        for p in handoff.table_profiles:
            for col in p.get("columns", []):
                if not isinstance(col, dict):
                    continue
                col_name = col.get("column_name", col.get("name", "")).upper()
                if not any(kw in col_name for kw in ("DATE", "_DT", "_TS", "TIMESTAMP")):
                    continue
                col_max = col.get("max", "")
                if col_max and isinstance(col_max, str) and (not max_date or col_max > max_date):
                    max_date = col_max

        if max_date:
            dq_questions = sections.get(IntakeSection.DATA_QUALITY.value, [])
            for q in dq_questions:
                if q.question_id == "dq_freshness":
                    if not q.auto_populated:
                        q.auto_populated = True
                        q.auto_value = f"Latest data: {max_date}"
                    break
