import asyncio
from datetime import datetime, timedelta
from typing import List, Optional
import math

import httpx
from fastapi import BackgroundTasks, Depends, FastAPI, HTTPException, Query, status
from fastapi.middleware.cors import CORSMiddleware
from sqlalchemy import func, select, update, desc, and_, or_
from sqlalchemy.ext.asyncio import AsyncSession

from services.emergency_service.models import EmergencyAlert, EmergencyResponse, EmergencyReport, SafetyCheck
from services.emergency_service.schemas import (
    AlertStatus,
    AlertType,
    EmergencyAlertCreate,
    EmergencyAlertResponse,
    EmergencyAlertUpdate,
    EmergencyResponseCreate,
    EmergencyResponseResponse,
    EmergencyStatistics,
    EmergencyReportCreate,
    EmergencyReportResponse,
    NearbyAlertResponse,
    SafetyCheckCreate,
    SafetyCheckResponse,
)
# Упрощенная модель User для Emergency Service 
from sqlalchemy import Column, Integer, String, Boolean
from shared.database import BaseModel

class User(BaseModel):
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String, unique=True, index=True)
    email = Column(String, unique=True, index=True)
    is_active = Column(Boolean, default=True)

from shared.auth import get_current_user_from_token
from shared.config import settings
from shared.database import AsyncSessionLocal

# Database dependency
async def get_db():
    async with AsyncSessionLocal() as session:
        try:
            yield session
        except Exception:
            await session.rollback()
            raise
        finally:
            await session.close()

app = FastAPI(title="Emergency Service", version="1.0.0")

# CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


async def get_current_user(
    user_data: dict = Depends(get_current_user_from_token),
    db: AsyncSession = Depends(get_db),
):
    """Get current user from token via auth dependency."""
    # Get full user object from database
    result = await db.execute(select(User).filter(User.id == user_data["user_id"]))
    user = result.scalars().first()
    if user is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
        )
    return user


def calculate_distance(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    """Calculate distance between two points in kilometers using Haversine formula."""
    # Convert latitude and longitude from degrees to radians
    lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
    
    # Haversine formula
    dlat = lat2 - lat1
    dlon = lon2 - lon1
    a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
    c = 2 * math.asin(math.sqrt(a))
    
    # Radius of earth in kilometers
    r = 6371
    return c * r

@app.get("/health")
async def health_check():
    """Health check endpoint"""
    return {"status": "healthy", "service": "emergency_service"}


async def get_nearby_users(
    latitude: float, longitude: float, radius_km: float = 1.0
) -> List[dict]:
    """Get users within radius using Location Service"""
    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(
                f"http://localhost:8003/api/v1/nearby-users",
                params={
                    "latitude": latitude,
                    "longitude": longitude,
                    "radius_km": radius_km,
                },
                timeout=5.0,
            )
            if response.status_code == 200:
                return response.json()
            return []
        except Exception:
            return []


async def send_emergency_notifications(alert_id: int, nearby_users: List[dict]):
    """Send push notifications to nearby users"""
    async with httpx.AsyncClient() as client:
        try:
            await client.post(
                "http://localhost:8005/api/v1/send-emergency-notifications",
                json={
                    "alert_id": alert_id,
                    "user_ids": [user["user_id"] for user in nearby_users],
                },
                timeout=10.0,
            )
        except Exception as e:
            print(f"Failed to send notifications: {e}")


@app.post("/api/v1/alert", response_model=EmergencyAlertResponse)
async def create_emergency_alert(
    alert_data: EmergencyAlertCreate,
    background_tasks: BackgroundTasks,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Create new emergency alert"""
    # Create alert
    db_alert = EmergencyAlert(
        user_id=current_user.id,
        latitude=alert_data.latitude,
        longitude=alert_data.longitude,
        address=alert_data.address,
        alert_type=alert_data.alert_type,
        message=alert_data.message,
    )

    db.add(db_alert)
    await db.commit()
    await db.refresh(db_alert)

    # Process alert in background
    background_tasks.add_task(
        process_emergency_alert_in_background,
        int(db_alert.id),  # Convert to int explicitly
        alert_data.latitude, 
        alert_data.longitude
    )

    return EmergencyAlertResponse.model_validate(db_alert)


async def process_emergency_alert_in_background(alert_id: int, latitude: float, longitude: float):
    """Process emergency alert - notify nearby users"""
    try:
        # Get nearby users
        nearby_users = await get_nearby_users(latitude, longitude)

        if nearby_users:
            # Create new database session for background task
            from shared.database import AsyncSessionLocal
            async with AsyncSessionLocal() as db:
                try:
                    # Update alert with notification count
                    await db.execute(
                        update(EmergencyAlert)
                        .where(EmergencyAlert.id == alert_id)
                        .values(notified_users_count=len(nearby_users))
                    )
                    await db.commit()

                    # Send notifications
                    await send_emergency_notifications(alert_id, nearby_users)

                except Exception as e:
                    print(f"Error processing emergency alert: {e}")
                    await db.rollback()

    except Exception as e:
        print(f"Error in process_emergency_alert_in_background: {e}")


@app.post("/api/v1/alert/{alert_id}/respond", response_model=EmergencyResponseResponse)
async def respond_to_alert(
    alert_id: int,
    response_data: EmergencyResponseCreate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Respond to emergency alert"""
    # Check if alert exists
    result = await db.execute(select(EmergencyAlert).filter(EmergencyAlert.id == alert_id))
    alert = result.scalars().first()
    
    if not alert:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Alert not found"
        )

    # Check if already responded
    existing_response = await db.execute(
        select(EmergencyResponse).filter(
            EmergencyResponse.alert_id == alert_id,
            EmergencyResponse.responder_id == current_user.id
        )
    )
    if existing_response.scalars().first():
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="You have already responded to this alert"
        )

    # Create response
    db_response = EmergencyResponse(
        alert_id=alert_id,
        responder_id=current_user.id,
        response_type=response_data.response_type,
        message=response_data.message,
        eta_minutes=response_data.eta_minutes,
    )

    db.add(db_response)
    
    # Update responded users count
    await db.execute(
        update(EmergencyAlert)
        .where(EmergencyAlert.id == alert_id)
        .values(responded_users_count=EmergencyAlert.responded_users_count + 1)
    )
    
    await db.commit()
    await db.refresh(db_response)

    # Create response with responder info
    response_dict = db_response.__dict__.copy()
    response_dict['responder_name'] = current_user.username
    response_dict['responder_phone'] = getattr(current_user, 'phone_number', None)

    return EmergencyResponseResponse.model_validate(response_dict)


@app.put("/api/v1/alert/{alert_id}/resolve")
async def resolve_alert(
    alert_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db),
):
    """Resolve emergency alert"""
    result = await db.execute(select(EmergencyAlert).filter(EmergencyAlert.id == alert_id))
    alert = result.scalars().first()
    
    if not alert:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Alert not found"
        )

    # Only alert creator can resolve
    if alert.user_id != current_user.id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Only alert creator can resolve the alert"
        )

    # Update alert
    await db.execute(
        update(EmergencyAlert)
        .where(EmergencyAlert.id == alert_id)
        .values(
            is_resolved=True,
            resolved_at=datetime.utcnow(),
            resolved_by=current_user.id
        )
    )
    await db.commit()

    return {"message": "Alert resolved successfully"}


@app.get("/api/v1/alerts/my", response_model=List[EmergencyAlertResponse])
async def get_my_alerts(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get current user's emergency alerts"""
    result = await db.execute(
        select(EmergencyAlert)
        .filter(EmergencyAlert.user_id == current_user.id)
        .order_by(EmergencyAlert.created_at.desc())
    )
    alerts = result.scalars().all()
    
    return [EmergencyAlertResponse.model_validate(alert) for alert in alerts]


@app.get("/api/v1/alerts/active", response_model=List[EmergencyAlertResponse])
async def get_active_alerts(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get active emergency alerts"""
    result = await db.execute(
        select(EmergencyAlert)
        .filter(EmergencyAlert.is_resolved == False)
        .order_by(EmergencyAlert.created_at.desc())
        .limit(50)
    )
    alerts = result.scalars().all()
    
    return [EmergencyAlertResponse.model_validate(alert) for alert in alerts]


@app.get("/api/v1/emergency/reports", response_model=List[EmergencyAlertResponse])
async def get_emergency_reports(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get emergency reports/alerts for admin users or general statistics"""
    # For now, return all active alerts (in production, add admin check)
    result = await db.execute(
        select(EmergencyAlert)
        .filter(EmergencyAlert.is_resolved == False)
        .order_by(EmergencyAlert.created_at.desc())
        .limit(50)
    )
    alerts = result.scalars().all()
    
    return [EmergencyAlertResponse.model_validate(alert) for alert in alerts]


@app.get("/api/v1/stats", response_model=EmergencyStatistics)
async def get_emergency_stats(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get emergency statistics"""
    # Get total alerts count
    total_alerts_result = await db.execute(select(func.count(EmergencyAlert.id)))
    total_alerts = total_alerts_result.scalar() or 0
    
    # Get active alerts count  
    active_alerts_result = await db.execute(
        select(func.count(EmergencyAlert.id)).filter(EmergencyAlert.is_resolved == False)
    )
    active_alerts = active_alerts_result.scalar() or 0
    
    # Calculate resolved alerts
    resolved_alerts = total_alerts - active_alerts
    
    # Get total responders count
    total_responders_result = await db.execute(select(func.count(EmergencyResponse.id)))
    total_responders = total_responders_result.scalar() or 0

    return EmergencyStatistics(
        total_alerts=total_alerts,
        active_alerts=active_alerts,
        resolved_alerts=resolved_alerts,
        avg_response_time_minutes=0,  # TODO: Calculate this
        total_responders=total_responders,
    )


@app.get("/api/v1/alerts/nearby", response_model=List[NearbyAlertResponse])
async def get_nearby_alerts(
    latitude: float = Query(..., ge=-90, le=90),
    longitude: float = Query(..., ge=-180, le=180),
    radius_km: float = Query(default=10.0, ge=0.1, le=100),
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get nearby emergency alerts within specified radius"""
    # For now, return all active alerts (in production, add distance filtering)
    result = await db.execute(
        select(EmergencyAlert)
        .filter(EmergencyAlert.is_resolved == False)
        .order_by(EmergencyAlert.created_at.desc())
        .limit(20)
    )
    alerts = result.scalars().all()
    
    nearby_alerts = []
    for alert in alerts:
        distance = calculate_distance(latitude, longitude, alert.latitude, alert.longitude)
        if distance <= radius_km:
            nearby_alerts.append(NearbyAlertResponse(
                id=alert.id,
                alert_type=alert.alert_type,
                latitude=alert.latitude,
                longitude=alert.longitude,
                address=alert.address,
                distance_km=round(distance, 2),
                created_at=alert.created_at,
                responded_users_count=alert.responded_users_count or 0
            ))
    
    return sorted(nearby_alerts, key=lambda x: x.distance_km)


@app.post("/api/v1/report", response_model=EmergencyReportResponse)
async def create_emergency_report(
    report_data: EmergencyReportCreate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Create emergency report"""
    db_report = EmergencyReport(
        user_id=current_user.id if not report_data.is_anonymous else None,
        latitude=report_data.latitude,
        longitude=report_data.longitude,
        address=report_data.address,
        report_type=report_data.report_type,
        description=report_data.description,
        is_anonymous=report_data.is_anonymous,
        severity=report_data.severity
    )
    
    db.add(db_report)
    await db.commit()
    await db.refresh(db_report)
    
    return EmergencyReportResponse.model_validate(db_report)


@app.get("/api/v1/reports", response_model=List[EmergencyReportResponse])
async def get_emergency_reports(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get emergency reports"""
    result = await db.execute(
        select(EmergencyReport)
        .order_by(EmergencyReport.created_at.desc())
        .limit(50)
    )
    reports = result.scalars().all()
    
    return [EmergencyReportResponse.model_validate(report) for report in reports]


@app.post("/api/v1/safety-check", response_model=SafetyCheckResponse)
async def create_safety_check(
    check_data: SafetyCheckCreate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Create safety check-in"""
    db_check = SafetyCheck(
        user_id=current_user.id,
        message=check_data.message,
        location_latitude=check_data.location_latitude,
        location_longitude=check_data.location_longitude
    )
    
    db.add(db_check)
    await db.commit()
    await db.refresh(db_check)
    
    return SafetyCheckResponse.model_validate(db_check)


@app.get("/api/v1/safety-checks", response_model=List[SafetyCheckResponse])
async def get_safety_checks(
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get user's safety check-ins"""
    result = await db.execute(
        select(SafetyCheck)
        .filter(SafetyCheck.user_id == current_user.id)
        .order_by(SafetyCheck.created_at.desc())
        .limit(50)
    )
    checks = result.scalars().all()
    
    return [SafetyCheckResponse.model_validate(check) for check in checks]


@app.put("/api/v1/alert/{alert_id}", response_model=EmergencyAlertResponse)
async def update_emergency_alert(
    alert_id: int,
    update_data: EmergencyAlertUpdate,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Update emergency alert"""
    result = await db.execute(select(EmergencyAlert).filter(EmergencyAlert.id == alert_id))
    alert = result.scalars().first()
    
    if not alert:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Alert not found"
        )

    # Only alert creator can update
    if alert.user_id != current_user.id:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN,
            detail="Only alert creator can update the alert"
        )

    # Update fields
    update_dict = {}
    if update_data.is_resolved is not None:
        update_dict['is_resolved'] = update_data.is_resolved
        if update_data.is_resolved:
            update_dict['resolved_at'] = datetime.utcnow()
            update_dict['resolved_by'] = current_user.id
    
    if update_data.message is not None:
        update_dict['message'] = update_data.message

    if update_dict:
        await db.execute(
            update(EmergencyAlert)
            .where(EmergencyAlert.id == alert_id)
            .values(**update_dict)
        )
        await db.commit()
        await db.refresh(alert)

    return EmergencyAlertResponse.model_validate(alert)


@app.get("/api/v1/alert/{alert_id}/responses", response_model=List[EmergencyResponseResponse])
async def get_alert_responses(
    alert_id: int,
    current_user: User = Depends(get_current_user),
    db: AsyncSession = Depends(get_db)
):
    """Get responses for specific alert"""
    # Check if alert exists
    result = await db.execute(select(EmergencyAlert).filter(EmergencyAlert.id == alert_id))
    alert = result.scalars().first()
    
    if not alert:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND, detail="Alert not found"
        )

    # Get responses
    responses_result = await db.execute(
        select(EmergencyResponse)
        .filter(EmergencyResponse.alert_id == alert_id)
        .order_by(EmergencyResponse.created_at.desc())
    )
    responses = responses_result.scalars().all()
    
    return [EmergencyResponseResponse.model_validate(response) for response in responses]


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8002)