"""Dagster assets for company monitoring data."""

import os
import re
from dagster import (
    asset,
    AssetExecutionContext,
    FreshnessPolicy,
    AutoMaterializePolicy,
    MetadataValue,
)
import psycopg2
from datetime import datetime
from typing import List, Dict, Any

import pdfplumber
from pymongo.errors import PyMongoError

from optovia_data.resources import (
    EgrulAgentResource,
    KycPostgresResource,
    MongoResource,
)


@asset(
    description="List of companies from KYC monitoring that need data refresh",
    group_name="monitoring",
)
def companies_to_check(kyc_postgres: KycPostgresResource) -> List[Dict[str, Any]]:
    """Get list of companies from KYC monitoring that need data refresh.

    Reads from kyc_monitoring joined with kyc_details_russia to get INN.
    Selects companies where updated_at is older than 90 days or never checked.
    """
    conn = psycopg2.connect(kyc_postgres.get_connection_string())
    try:
        with conn.cursor() as cur:
            cur.execute("""
                SELECT
                    m.id,
                    m.uuid,
                    r.inn,
                    r.company_name,
                    r.ogrn,
                    m.updated_at as last_check
                FROM kyc_monitoring m
                JOIN django_content_type ct ON m.content_type_id = ct.id
                JOIN kyc_details_russia r ON m.object_id = r.id
                WHERE ct.model = 'kycrequestrussia'
                  AND m.country_code = 'RU'
                  AND m.workflow_status = 'active'
                  AND (m.updated_at IS NULL OR m.updated_at < NOW() - INTERVAL '90 days')
                ORDER BY m.updated_at NULLS FIRST
            """)
            rows = cur.fetchall()
            return [
                {
                    "id": row[0],
                    "uuid": str(row[1]),
                    "inn": row[2],
                    "name": row[3],
                    "ogrn": row[4],
                    "last_check": row[5],
                }
                for row in rows
            ]
    finally:
        conn.close()


def parse_egrul_response(response_text: str) -> Dict[str, Any]:
    """Parse EGRUL agent response to extract structured data."""
    data = {
        "name": None,
        "ogrn": None,
        "pdf_path": None,
        "raw_response": response_text,
    }

    # Extract name - various formats from agent response
    name_patterns = [
        r'\*\*Название:\*\*\s*(.+?)(?:\n|\*|$)',
        r'Название(?:\s+(?:ИП|компании))?:\s*(.+?)(?:\n|$)',
    ]
    for pattern in name_patterns:
        name_match = re.search(pattern, response_text)
        if name_match:
            name = name_match.group(1).strip()
            if name and not name.isdigit():
                data["name"] = name
                break

    # Extract OGRN/OGRNIP
    ogrn_patterns = [
        r'\*\*(?:ОГРН|ОГРНИП):\*\*\s*(\d+)',
        r'(?:ОГРН|ОГРНИП):\s*(\d+)',
    ]
    for pattern in ogrn_patterns:
        ogrn_match = re.search(pattern, response_text)
        if ogrn_match:
            data["ogrn"] = ogrn_match.group(1)
            break

    # Extract PDF path
    pdf_match = re.search(r'(?:Путь.*PDF.*|PDF.*файл):\s*(.+\.pdf)', response_text, re.IGNORECASE)
    if pdf_match:
        data["pdf_path"] = pdf_match.group(1).strip()

    return data


def parse_pdf_to_json(pdf_path: str) -> Dict[str, Any]:
    """Parse PDF into a JSON-friendly structure (text + tables per page)."""
    if not pdf_path:
        return {"status": "skipped", "reason": "missing_pdf_path"}
    if not os.path.exists(pdf_path):
        return {"status": "skipped", "reason": "file_not_found", "path": pdf_path}

    result = {
        "status": "ok",
        "path": pdf_path,
        "pages": [],
    }
    try:
        with pdfplumber.open(pdf_path) as pdf:
            for page_number, page in enumerate(pdf.pages, start=1):
                result["pages"].append({
                    "page": page_number,
                    "text": page.extract_text() or "",
                    "tables": page.extract_tables() or [],
                })
    except Exception as exc:
        return {"status": "failed", "reason": str(exc), "path": pdf_path}

    return result


@asset(
    description="EGRUL data fetched from nalog.ru and stored in MongoDB",
    group_name="monitoring",
    freshness_policy=FreshnessPolicy(maximum_lag_minutes=90 * 24 * 60),  # 90 days
    auto_materialize_policy=AutoMaterializePolicy.eager(),
)
def egrul_data(
    context: AssetExecutionContext,
    companies_to_check: List[Dict[str, Any]],
    egrul_agent: EgrulAgentResource,
    kyc_postgres: KycPostgresResource,
    mongo: MongoResource,
) -> Dict[str, Any]:
    """Fetch EGRUL data for companies and store in MongoDB.

    Documents are stored in 'company_documents' collection with structure:
    {
        "inn": "1234567890",
        "source": "egrul",
        "monitoring_uuid": "uuid",
        "data": { ... parsed EGRUL data ... },
        "collected_at": ISODate
    }
    """
    results = {
        "processed": 0,
        "success": 0,
        "failed": 0,
        "companies": [],
    }

    if not companies_to_check:
        context.log.info("No companies to check - all data is fresh")
        return results

    if not mongo.uri:
        context.log.error("MONGODB_URI is not set; cannot store data")
        return results

    mongo_collection = mongo.get_collection("company_documents")
    kyc_conn = psycopg2.connect(kyc_postgres.get_connection_string())

    try:
        for company in companies_to_check:
            inn = company["inn"]
            monitoring_uuid = company["uuid"]
            context.log.info(f"Processing INN: {inn} (name: {company.get('name', 'unknown')})")

            try:
                # Call EGRUL agent
                response = egrul_agent.fetch_company(inn)

                # Parse response
                parsed = parse_egrul_response(response.get("content", ""))

                # Parse PDF to JSON
                pdf_json = parse_pdf_to_json(parsed.get("pdf_path"))

                # Store document in MongoDB
                doc = {
                    "inn": inn,
                    "source": "egrul",
                    "monitoring_uuid": monitoring_uuid,
                    "data": {
                        "name": parsed["name"],
                        "ogrn": parsed["ogrn"],
                        "pdf_path": parsed["pdf_path"],
                        "raw_response": parsed["raw_response"],
                        "pdf_content": pdf_json,
                    },
                    "collected_at": datetime.utcnow(),
                }

                # Upsert by inn + source
                mongo_collection.update_one(
                    {"inn": inn, "source": "egrul"},
                    {
                        "$set": doc,
                        "$setOnInsert": {"created_at": datetime.utcnow()},
                    },
                    upsert=True,
                )

                # Update kyc_monitoring.updated_at to mark as checked
                with kyc_conn.cursor() as cur:
                    cur.execute("""
                        UPDATE kyc_monitoring
                        SET updated_at = NOW()
                        WHERE uuid = %s
                    """, (monitoring_uuid,))
                kyc_conn.commit()

                results["success"] += 1
                results["companies"].append({
                    "inn": inn,
                    "name": parsed["name"],
                    "ogrn": parsed["ogrn"],
                    "status": "success",
                })
                context.log.info(f"Successfully processed {inn}: {parsed['name']}")

            except PyMongoError as exc:
                context.log.error(f"MongoDB error for {inn}: {exc}")
                results["failed"] += 1
                results["companies"].append({
                    "inn": inn,
                    "status": "failed",
                    "error": str(exc),
                })

            except Exception as e:
                kyc_conn.rollback()
                results["failed"] += 1
                results["companies"].append({
                    "inn": inn,
                    "status": "failed",
                    "error": str(e),
                })
                context.log.error(f"Failed to process {inn}: {e}")

            results["processed"] += 1

    finally:
        kyc_conn.close()
        mongo_collection.database.client.close()

    # Add metadata for UI
    context.add_output_metadata({
        "total_to_check": len(companies_to_check),
        "processed_count": results["processed"],
        "success_count": results["success"],
        "failed_count": results["failed"],
        "timestamp": MetadataValue.text(datetime.now().isoformat()),
    })

    return results
