from datetime import datetime, timezone

from django.http import HttpResponse, JsonResponse
from timezonefinder import TimezoneFinder

import swisseph as swe

from .calc import (
    DEFAULT_HOUSE_SYSTEM,
    calculate_aspects,
    get_element_counts,
    get_julian_day,
    get_planet_positions,
    set_ephe_path,
)
from .models import EphemerisSnapshot


def chart(request):
    dt_str = request.GET.get('dt')
    lat_str = request.GET.get('lat')
    lon_str = request.GET.get('lon')

    if not dt_str or lat_str is None or lon_str is None:
        return HttpResponse(status=400)

    try:
        dt = datetime.fromisoformat(dt_str.replace('Z', '+00:00'))
    except ValueError:
        return HttpResponse(status=400)

    try:
        lat = float(lat_str)
        lon = float(lon_str)
    except ValueError:
        return HttpResponse(status=400)

    if not (-90 <= lat <= 90):
        return HttpResponse(status=400)

    house_system_param = request.GET.get('house_system')
    if house_system_param is not None:
        if not (hasattr(request, 'user') and request.user.is_authenticated
                and request.user.is_superuser):
            return HttpResponse(status=403)
        house_system = house_system_param
    else:
        house_system = DEFAULT_HOUSE_SYSTEM

    set_ephe_path()

    jd = get_julian_day(dt)
    planets = get_planet_positions(jd)

    cusps, ascmc = swe.houses(jd, lat, lon, house_system.encode())
    houses = {
        'cusps': list(cusps),
        'asc': ascmc[0],
        'mc': ascmc[1],
    }

    return JsonResponse({
        'planets': planets,
        'houses': houses,
        'elements': get_element_counts(planets),
        'aspects': calculate_aspects(planets),
        'house_system': house_system,
    })


_tf = TimezoneFinder()


def timezone_lookup(request):
    """GET /api/tz/ — resolve IANA timezone string from lat/lon.

    Query params: lat (float), lon (float)
    Returns: { "timezone": "America/New_York" }
    Returns 404 JSON { "timezone": null } if coordinates fall in international
    waters (no timezone found) — not an error, just no result.
    """
    lat_str = request.GET.get('lat')
    lon_str = request.GET.get('lon')

    if lat_str is None or lon_str is None:
        return HttpResponse(status=400)

    try:
        lat = float(lat_str)
        lon = float(lon_str)
    except ValueError:
        return HttpResponse(status=400)

    if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
        return HttpResponse(status=400)

    tz = _tf.timezone_at(lat=lat, lng=lon)
    return JsonResponse({'timezone': tz})


def charts_list(request):
    date_from_str = request.GET.get('date_from')
    date_to_str = request.GET.get('date_to')

    if not date_from_str or not date_to_str:
        return HttpResponse(status=400)

    try:
        date_from = datetime.strptime(date_from_str, '%Y-%m-%d').replace(
            tzinfo=timezone.utc)
        date_to = datetime.strptime(date_to_str, '%Y-%m-%d').replace(
            hour=23, minute=59, second=59, tzinfo=timezone.utc)
    except ValueError:
        return HttpResponse(status=400)

    if date_to < date_from:
        return HttpResponse(status=400)

    qs = EphemerisSnapshot.objects.filter(dt__gte=date_from, dt__lte=date_to)

    element_fields = {
        'fire_min': 'fire', 'water_min': 'water',
        'earth_min': 'earth', 'air_min': 'air',
        'time_min': 'time_el', 'space_min': 'space_el',
    }
    for param, field in element_fields.items():
        value = request.GET.get(param)
        if value is not None:
            try:
                qs = qs.filter(**{f'{field}__gte': int(value)})
            except ValueError:
                return HttpResponse(status=400)

    results = [
        {
            'dt': snap.dt.isoformat(),
            'elements': snap.elements_dict(),
            'planets': snap.chart_data.get('planets', {}),
        }
        for snap in qs
    ]

    return JsonResponse({'results': results, 'count': len(results)})
