from typing import Optional, Tuple, List, Dict, Any
from datetime import datetime, time
import requests
import re

from sqlalchemy.orm import Session
from sqlalchemy import cast, Date

from . import dto, models
import urllib3
from core.config import settings

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)




class ExchangeRateService:
    """Service for ExchangeRate CRUD operations with tenant support"""

    @staticmethod
    def get_all(
        db: Session,
        tenant_id: int,
        company_id: int,
        skip: int = 0,
        limit: int = 50,
        filters: Optional[Dict[str, Any]] = None,
    ) -> Tuple[List[models.ExchangeRate], int]:
        """Get all exchange rates for a tenant/company with pagination"""
        query = db.query(models.ExchangeRate).filter(
            models.ExchangeRate.tenant_id == tenant_id,
            models.ExchangeRate.company_id == company_id,
        )

        # Apply filters if provided
        if filters:
            if filters.get("date"):
                # Use range query to utilize index on (tenant_id, company_id, date) efficiently
                # filters["date"] is expected to be 'YYYY-MM-DD'
                try:
                    date_str = filters["date"]
                    date_val = datetime.strptime(date_str, "%Y-%m-%d").date()
                    start_date = datetime.combine(date_val, time.min)
                    end_date = datetime.combine(date_val, time.max)

                    query = query.filter(
                        models.ExchangeRate.date >= start_date,
                        models.ExchangeRate.date <= end_date,
                    )
                except (ValueError, TypeError):
                    # Fallback to cast if date format is invalid or logic fails, though validation should catch this
                    query = query.filter(
                        cast(models.ExchangeRate.date, Date) == filters["date"]
                    )
            if filters.get("local_currency"):
                query = query.filter(
                    models.ExchangeRate.local_currency == filters["local_currency"]
                )
            if filters.get("foreign_currency"):
                query = query.filter(
                    models.ExchangeRate.foreign_currency == filters["foreign_currency"]
                )

        total = query.count()
        exchange_rates = (
            query.order_by(models.ExchangeRate.date.desc())
            .offset(skip)
            .limit(limit)
            .all()
        )

        return exchange_rates, total

    @staticmethod
    def get_by_id(
        db: Session, exchange_rate_id: int, tenant_id: int, company_id: int
    ) -> Optional[models.ExchangeRate]:
        """Get exchange rate by ID"""
        return (
            db.query(models.ExchangeRate)
            .filter(
                models.ExchangeRate.id == exchange_rate_id,
                models.ExchangeRate.tenant_id == tenant_id,
                models.ExchangeRate.company_id == company_id,
            )
            .first()
        )

    @staticmethod
    def create(
        db: Session,
        exchange_rate_data: dto.ExchangeRateCreateDTO,
        tenant_id: int,
        company_id: int,
    ) -> models.ExchangeRate:
        """Create a new exchange rate"""
        new_exchange_rate = models.ExchangeRate(
            **exchange_rate_data.model_dump(),
            tenant_id=tenant_id,
            company_id=company_id
        )
        db.add(new_exchange_rate)
        db.commit()
        db.refresh(new_exchange_rate)
        return new_exchange_rate

    @staticmethod
    def update(
        db: Session,
        exchange_rate_id: int,
        tenant_id: int,
        exchange_rate_data: dto.ExchangeRateUpdateDTO,
        company_id: int,
    ) -> Optional[models.ExchangeRate]:
        """Update an exchange rate"""
        exchange_rate = ExchangeRateService.get_by_id(
            db, exchange_rate_id, tenant_id, company_id
        )
        if not exchange_rate:
            return None

        # Update fields
        update_data = exchange_rate_data.model_dump(exclude_unset=True)
        for field, value in update_data.items():
            setattr(exchange_rate, field, value)

        db.commit()
        db.refresh(exchange_rate)
        return exchange_rate

    @staticmethod
    def delete(
        db: Session, exchange_rate_id: int, tenant_id: int, company_id: int
    ) -> bool:
        """Delete an exchange rate"""
        exchange_rate = ExchangeRateService.get_by_id(
            db, exchange_rate_id, tenant_id, company_id
        )
        if not exchange_rate:
            return False

        db.delete(exchange_rate)
        db.commit()
        return True

    @staticmethod
    def _get_sitar_api_token(base_url, username, password) -> Optional[str]:
        """Helper to get authentication token from external API"""
        try:
            print(base_url)
            login_url = f"{base_url}/exchange-rate/auth/login"
            payload = {"username": username, "password": password}
            headers = {"Content-Type": "application/json"}
            
            response = requests.post(login_url, json=payload, headers=headers, timeout=5)
            if response.status_code not in [200, 201]:
                print(f"External API Login Failed: {response.status_code} - {response.text}")
                return None
                
            data = response.json()
            return data.get("token") or data.get("access_token")
        except Exception as e:
            print(f"External API Login Error: {e}")
            return None

    @staticmethod
    def fetch_from_dof(date_str: str) -> Optional[float]:
        """
        Fetches the exchange rate from an external API (replacing direct DOF scraping).
        The API handles date logic (holidays, weekends) automatically.
        
        Args:
            date_str (str): Date in 'YYYY-MM-DD' format.
            
        Returns:
            Optional[float]: The exchange rate value if found, None otherwise.
        """
        # API Credentials
        API_BASE_URL = settings.SITAR_API_URL
        API_USER = settings.SITAR_API_USER
        API_PASS = settings.SITAR_API_PASSWORD
        
        if not API_USER or not API_PASS:
            print("ERROR: External API credentials not properly configured in settings")
            return None

        try:
            print(f"DEBUG: Fetching External API for date: {date_str}")
            
            # 1. Get Token
            token = ExchangeRateService._get_sitar_api_token(API_BASE_URL, API_USER, API_PASS)
            if not token:
                print("Failed to obtain external API token")
                return None
                
            # 2. Fetch Exchange Rate
            # The API endpoint is /tipoCambio/{YYYY-MM-DD}
            tc_endpoint = f"{API_BASE_URL}/exchange-rate/tipoCambio/{date_str}"
            
            # Auth header: The API expects just the token string in common usage, but we try standard first
            # based on user feedback/code: 'Authorization:' . $token
            headers = {
                "Authorization": token,
                "Content-Type": "application/json"
            }
            
            response = requests.get(tc_endpoint, headers=headers, timeout=5)
            
            # Retry logic as per PHP reference (if 401, maybe formatting issue, but requests handles headers well)
            if response.status_code == 401:
                 # Try with Bearer prefix just in case, though PHP code suggested raw token
                 print("DEBUG: 401 received, retrying with Bearer prefix...")
                 headers["Authorization"] = f"Bearer {token}"
                 response = requests.get(tc_endpoint, headers=headers, timeout=5)

            if response.status_code != 200:
                print(f"External API TC Error: {response.status_code} - {response.text}")
                return None
                
            data = response.json()
            # Expected response: {"Id":..., "Fecha":"...", "TipoCambio":17.452, "Mov":"..."}
            
            if "TipoCambio" in data:
                val = float(data["TipoCambio"])
                print(f"DEBUG: External API returned value: {val}")
                return val
            
            print(f"DEBUG: 'TipoCambio' key not found in response: {data}")
            return None
            
        except Exception as e:
            print(f"Error fetching from External API: {e}")
            return None
