from fastapi import FastAPI, APIRouter, HTTPException, Request
from fastapi.middleware.cors import CORSMiddleware
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseModel, Field, validator, EmailStr
from dotenv import load_dotenv
from pathlib import Path
from typing import List, Optional
from datetime import datetime
import os
import logging
import re
import uuid

ROOT_DIR = Path(__file__).parent
load_dotenv(ROOT_DIR / '.env')

# MongoDB connection
mongo_url = os.environ['MONGO_URL']
client = AsyncIOMotorClient(mongo_url)
db = client[os.environ['DB_NAME']]

# Create the main app without a prefix
app = FastAPI(title="Solaire France Pro API", version="1.0.0")

# Create a router with the /api prefix
api_router = APIRouter(prefix="/api")

# Models for Solar Panel Company
class ContactSubmissionCreate(BaseModel):
    firstName: str = Field(..., min_length=2, max_length=50)
    lastName: str = Field(..., min_length=2, max_length=50)
    email: EmailStr
    phone: str = Field(..., min_length=10, max_length=15)
    postalCode: str = Field(..., min_length=5, max_length=5)
    housingType: str = Field(..., pattern="^(maison|appartement|ferme|local)$")
    message: Optional[str] = Field(None, max_length=500)
    gdprConsent: bool = Field(..., description="Must be True")

    @validator('firstName', 'lastName')
    def validate_names(cls, v):
        if not re.match(r'^[a-zA-ZÀ-ÿ\s\-\']+$', v):
            raise ValueError('Name must contain only letters, spaces, hyphens and apostrophes')
        return v.strip()

    @validator('phone')
    def validate_phone(cls, v):
        # French phone number validation
        phone_clean = re.sub(r'[^\d]', '', v)
        if not re.match(r'^0[1-9](\d{8})$', phone_clean):
            raise ValueError('Invalid French phone number format')
        return phone_clean

    @validator('postalCode')
    def validate_postal_code(cls, v):
        if not re.match(r'^\d{5}$', v):
            raise ValueError('Postal code must be 5 digits')
        return v

    @validator('gdprConsent')
    def validate_gdpr_consent(cls, v):
        if not v:
            raise ValueError('GDPR consent is required')
        return v

class ContactSubmission(ContactSubmissionCreate):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    status: str = Field(default="nouveau")
    createdAt: datetime = Field(default_factory=datetime.utcnow)
    updatedAt: datetime = Field(default_factory=datetime.utcnow)

class NewsletterSubscriptionCreate(BaseModel):
    email: EmailStr

class NewsletterSubscription(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))
    email: EmailStr
    subscribedAt: datetime = Field(default_factory=datetime.utcnow)
    isActive: bool = Field(default=True)

class ApiResponse(BaseModel):
    success: bool
    message: str
    data: Optional[dict] = None

# API Routes
@api_router.get("/")
async def root():
    return {"message": "Solaire France Pro API", "status": "operational"}

@api_router.post("/contact", response_model=ApiResponse)
async def create_contact_submission(submission: ContactSubmissionCreate, request: Request):
    try:
        # Create submission object
        submission_dict = submission.dict()
        submission_obj = ContactSubmission(**submission_dict)
        
        # Insert into database
        result = await db.contact_submissions.insert_one(submission_obj.dict())
        
        if result.inserted_id:
            return ApiResponse(
                success=True,
                message="Votre demande a été envoyée avec succès ! Nous vous recontacterons sous 48h.",
                data={"id": submission_obj.id}
            )
        else:
            raise HTTPException(status_code=500, detail="Failed to save submission")
            
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logging.error(f"Error creating contact submission: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@api_router.get("/contact", response_model=List[ContactSubmission])
async def get_contact_submissions(limit: int = 100):
    try:
        submissions = await db.contact_submissions.find().sort("createdAt", -1).limit(limit).to_list(limit)
        return [ContactSubmission(**submission) for submission in submissions]
    except Exception as e:
        logging.error(f"Error fetching contact submissions: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@api_router.get("/contact/{submission_id}", response_model=ContactSubmission)
async def get_contact_submission(submission_id: str):
    try:
        submission = await db.contact_submissions.find_one({"id": submission_id})
        if not submission:
            raise HTTPException(status_code=404, detail="Submission not found")
        return ContactSubmission(**submission)
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"Error fetching contact submission: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@api_router.put("/contact/{submission_id}", response_model=ApiResponse)
async def update_contact_submission_status(submission_id: str, status: str):
    try:
        if status not in ["nouveau", "en_cours", "traité"]:
            raise HTTPException(status_code=400, detail="Invalid status")
            
        result = await db.contact_submissions.update_one(
            {"id": submission_id},
            {"$set": {"status": status, "updatedAt": datetime.utcnow()}}
        )
        
        if result.matched_count == 0:
            raise HTTPException(status_code=404, detail="Submission not found")
            
        return ApiResponse(
            success=True,
            message="Status updated successfully"
        )
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"Error updating contact submission: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@api_router.post("/newsletter", response_model=ApiResponse)
async def subscribe_newsletter(subscription: NewsletterSubscriptionCreate):
    try:
        # Check if email already exists
        existing = await db.newsletter_subscriptions.find_one({"email": subscription.email})
        if existing:
            if existing.get("isActive", True):
                return ApiResponse(
                    success=True,
                    message="Vous êtes déjà abonné à notre newsletter"
                )
            else:
                # Reactivate subscription
                await db.newsletter_subscriptions.update_one(
                    {"email": subscription.email},
                    {"$set": {"isActive": True, "subscribedAt": datetime.utcnow()}}
                )
                return ApiResponse(
                    success=True,
                    message="Votre abonnement a été réactivé avec succès"
                )
        
        # Create new subscription
        subscription_obj = NewsletterSubscription(email=subscription.email)
        result = await db.newsletter_subscriptions.insert_one(subscription_obj.dict())
        
        if result.inserted_id:
            return ApiResponse(
                success=True,
                message="Merci pour votre abonnement ! Vous recevrez nos dernières actualités.",
                data={"id": subscription_obj.id}
            )
        else:
            raise HTTPException(status_code=500, detail="Failed to save subscription")
            
    except ValueError as e:
        raise HTTPException(status_code=400, detail=str(e))
    except Exception as e:
        logging.error(f"Error creating newsletter subscription: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@api_router.delete("/newsletter/{email}", response_model=ApiResponse)
async def unsubscribe_newsletter(email: str):
    try:
        result = await db.newsletter_subscriptions.update_one(
            {"email": email},
            {"$set": {"isActive": False}}
        )
        
        if result.matched_count == 0:
            raise HTTPException(status_code=404, detail="Email not found in our newsletter list")
            
        return ApiResponse(
            success=True,
            message="Vous avez été désabonné avec succès de notre newsletter"
        )
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"Error unsubscribing from newsletter: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

@api_router.get("/newsletter", response_model=List[NewsletterSubscription])
async def get_newsletter_subscriptions(active_only: bool = True):
    try:
        query = {"isActive": True} if active_only else {}
        subscriptions = await db.newsletter_subscriptions.find(query).sort("subscribedAt", -1).to_list(1000)
        return [NewsletterSubscription(**sub) for sub in subscriptions]
    except Exception as e:
        logging.error(f"Error fetching newsletter subscriptions: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

# Include the router in the main app
app.include_router(api_router)

# Configure CORS
app.add_middleware(
    CORSMiddleware,
    allow_credentials=True,
    allow_origins=os.environ.get('CORS_ORIGINS', '*').split(','),
    allow_methods=["*"],
    allow_headers=["*"],
)

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

# Health check endpoint
@app.get("/health")
async def health_check():
    return {"status": "healthy", "timestamp": datetime.utcnow()}

@app.on_event("shutdown")
async def shutdown_db_client():
    client.close()
