from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from typing import List, Dict, Any
from uuid import UUID
from pydantic import BaseModel
import csv
import io
import json
import logging

from ...db import get_session
from ...auth import get_current_account
from ...services.roles import require_steward
from ...models import (
    ActionBatch,
    Investigation,
    Finding,
    ActionStatus,
    Integration,
    IntegrationProvider,
    IntegrationStatus,
    User,
)
from ...services.salesforce_gateway import SalesforceGateway, get_salesforce_gateway
from ...notifications.slack import send_slack
from ...services.audit_service import AuditService

router = APIRouter(prefix="/api/v2/actions", tags=["actions"])
log = logging.getLogger(__name__)


async def get_salesforce_integration(db: AsyncSession, account_id: str):
    """Deprecated helper; kept for backward compatibility. Use SalesforceGateway via DI."""
    stmt = select(Integration).where(
        Integration.account_id == account_id,
        Integration.provider == IntegrationProvider.SALESFORCE,
        Integration.status == IntegrationStatus.CONNECTED,
    )
    result = await db.execute(stmt)
    integration = result.scalar_one_or_none()
    if not integration:
        raise HTTPException(400, "Salesforce not connected")
    return integration


class PrepareActionRequest(BaseModel):
    investigation_id: str
    items: List[Dict[str, Any]]  # [{findingId, decision, payload}]


@router.post("/prepare")
async def prepare_actions(
    body: PrepareActionRequest,
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
    user: User = Depends(require_steward),  # Stewards can prepare actions
):
    """Prepare actions for review"""
    # Verify investigation
    investigation = await db.get(Investigation, body.investigation_id)
    if not investigation or investigation.account_id != str(account_id):
        raise HTTPException(404, "Investigation not found")

    # Get findings
    finding_ids = [item["findingId"] for item in body.items]
    stmt = select(Finding).where(Finding.id.in_(finding_ids))
    result = await db.execute(stmt)
    findings = {f.id: f for f in result.scalars()}

    # Build preview
    preview = {"deep_links": [], "field_updates": [], "csv_data": []}

    for item in body.items:
        finding = findings.get(item["findingId"])
        if not finding:
            continue

        if item["decision"] == "merge":
            # Build deep link for merge
            ids = [r["Id"] for r in finding.records]
            deep_link = f"https://yourinstance.salesforce.com/merge?ids={','.join(ids)}"
            preview["deep_links"].append(
                {
                    "type": "merge",
                    "object": finding.object_type,
                    "ids": ids,
                    "url": deep_link,
                }
            )

        elif item["decision"] == "link":
            # Preview field update
            preview["field_updates"].append(
                {
                    "object": "Lead",
                    "id": finding.suggestion["lead_id"],
                    "field": "Account__c",
                    "value": finding.suggestion["account_id"],
                }
            )

    # Create action batch
    batch = ActionBatch(
        investigation_id=body.investigation_id,
        created_by_user_id=str(account_id),
        items=body.items,
        preview=preview,
        status=ActionStatus.PREPARED,
    )
    db.add(batch)
    await db.commit()
    await db.refresh(batch)

    return {"batch_id": batch.id, "preview": preview, "item_count": len(body.items)}


class ApplyActionRequest(BaseModel):
    action_batch_id: str
    action_type: str  # "export_csv", "create_deeplinks", or "update_field"


@router.post("/apply")
async def apply_actions(
    body: ApplyActionRequest,
    request: Request,
    db: AsyncSession = Depends(get_session),
    account_id: UUID = Depends(get_current_account),
    user: User = Depends(require_steward),  # Stewards can apply actions
    sf: SalesforceGateway = Depends(get_salesforce_gateway),
):
    """Apply safe actions (CSV export or deep links only)"""
    # Log who performed the action for audit trail
    log.info(
        f"User {user.id} ({user.email}) applying {body.action_type} for batch {body.action_batch_id}"
    )
    batch = await db.get(ActionBatch, body.action_batch_id)
    if not batch:
        raise HTTPException(404, "Action batch not found")

    # Initialize audit service
    audit_service = AuditService(db)
    client_ip = request.client.host if request.client else None

    if body.action_type == "export_csv":
        # Generate CSV
        output = io.StringIO()
        writer = csv.DictWriter(
            output, fieldnames=["Type", "Object", "Records", "Score"]
        )
        writer.writeheader()

        record_ids = []
        for item in batch.items:
            finding = await db.get(Finding, item["findingId"])
            if finding:
                writer.writerow(
                    {
                        "Type": finding.category,
                        "Object": finding.object_type,
                        "Records": json.dumps(finding.records),
                        "Score": finding.score,
                    }
                )
                record_ids.extend([r.get("Id") for r in finding.records if r.get("Id")])

        output.seek(0)

        # Log audit event for export
        await audit_service.log_action(
            actor=user,
            action="export",
            object_type="Multiple",
            record_ids=record_ids,
            changes={},
            action_batch_id=body.action_batch_id,
            investigation_id=batch.investigation_id
            if hasattr(batch, "investigation_id")
            else None,
            request_ip=client_ip,
        )

        return StreamingResponse(
            io.BytesIO(output.getvalue().encode()),
            media_type="text/csv",
            headers={
                "Content-Disposition": f"attachment; filename=actions_{batch.id}.csv"
            },
        )

    elif body.action_type == "create_deeplinks":
        # Just return the deep links from preview
        return {
            "deep_links": batch.preview.get("deep_links", []),
            "message": "Open these links in Salesforce to perform actions",
        }

    elif body.action_type == "update_field":
        # Apply field updates via Salesforce Gateway

        updates_applied = []
        updates_failed = []

        for update in batch.preview.get("field_updates", []):
            # Only allow updating the Account lookup field for safety
            if update["field"] != "Account__c":
                log.warning(f"Skipping non-allowed field update: {update['field']}")
                continue

            # Update via Salesforce API with db callback for token refresh
            try:
                # Define database callback for token refresh
                async def save_integration(updated_integration):
                    await db.merge(updated_integration)
                    await db.commit()
                    await db.refresh(updated_integration)

                # Capture current value before update for rollback capability
                before_record = await sf.get_record(
                    update["object"], update["id"], [update["field"]]
                )
                before_val = before_record.get(update["field"])
                after_val = update["value"]

                result = await sf.update(
                    update["object"], update["id"], {update["field"]: update["value"]}
                )

                # Store snapshot for rollback
                snapshot = {
                    "object_type": update["object"],
                    "record_id": update["id"],
                    "field": update["field"],
                    "before": before_val,
                    "after": after_val,
                }
                batch.preview.setdefault("changes", []).append(snapshot)

                updates_applied.append({**update, "result": result})
                log.info(
                    f"Updated {update['object']} {update['id']}: {update['field']} = {update['value']}"
                )
            except Exception as e:
                log.error(f"Failed to update {update['id']}: {e}")
                updates_failed.append({**update, "error": str(e)})

        # Log audit event for field updates
        if updates_applied:
            changes = {}
            record_ids = []
            for update in updates_applied:
                record_key = f"{update['object']}:{update['id']}"
                record_ids.append(update["id"])
                changes[record_key] = {
                    update["field"]: {
                        "before": None,  # Could track previous value if needed
                        "after": update["value"],
                    }
                }

            await audit_service.log_action(
                actor=user,
                action="field_update",
                object_type="Lead",
                record_ids=record_ids,
                changes=changes,
                action_batch_id=body.action_batch_id,
                investigation_id=batch.investigation_id
                if hasattr(batch, "investigation_id")
                else None,
                request_ip=client_ip,
            )

        # Update batch status if all successful
        if updates_applied and not updates_failed:
            batch.status = ActionStatus.APPLIED
            await db.commit()

        # Send notification if configured
        if updates_applied:
            try:
                await send_slack(
                    "#salesforce-updates",
                    f"Applied {len(updates_applied)} Lead->Account field updates\n"
                    f"Success: {len(updates_applied)}, Failed: {len(updates_failed)}",
                )
            except Exception as e:
                log.warning(f"Failed to send Slack notification: {e}")

        return {
            "updates_applied": len(updates_applied),
            "updates_failed": len(updates_failed),
            "details": {"applied": updates_applied, "failed": updates_failed},
        }

    else:
        raise HTTPException(400, "Invalid action type")
