using LinqToDB;
using Azaion.Flights.Database;
using Azaion.Flights.Database.Entities;
using Azaion.Flights.DTOs;

namespace Azaion.Flights.Services;

public class FlightService(AppDataConnection db)
{
    public async Task<Flight> CreateFlight(CreateFlightRequest request)
    {
        var aircraftExists = await db.Aircrafts.AnyAsync(a => a.Id == request.AircraftId);
        if (!aircraftExists)
            throw new ArgumentException($"Aircraft {request.AircraftId} not found");

        var flight = new Flight
        {
            Id = Guid.NewGuid(),
            CreatedDate = request.CreatedDate ?? DateTime.UtcNow,
            Name = request.Name,
            AircraftId = request.AircraftId
        };
        await db.InsertAsync(flight);
        return flight;
    }

    public async Task<Flight> UpdateFlight(Guid id, UpdateFlightRequest request)
    {
        var flight = await db.Flights.FirstOrDefaultAsync(f => f.Id == id)
            ?? throw new KeyNotFoundException($"Flight {id} not found");

        if (request.Name != null)
            flight.Name = request.Name;
        if (request.AircraftId.HasValue)
        {
            var aircraftExists = await db.Aircrafts.AnyAsync(a => a.Id == request.AircraftId.Value);
            if (!aircraftExists)
                throw new ArgumentException($"Aircraft {request.AircraftId} not found");
            flight.AircraftId = request.AircraftId.Value;
        }

        await db.UpdateAsync(flight);
        return flight;
    }

    public async Task<Flight> GetFlight(Guid id)
    {
        var flight = await db.Flights.FirstOrDefaultAsync(f => f.Id == id)
            ?? throw new KeyNotFoundException($"Flight {id} not found");
        return flight;
    }

    public async Task<PaginatedResponse<Flight>> GetFlights(GetFlightsQuery query)
    {
        var q = db.Flights.AsQueryable();

        if (!string.IsNullOrEmpty(query.Name))
            q = q.Where(f => f.Name.ToLower().Contains(query.Name.ToLower()));
        if (query.FromDate.HasValue)
            q = q.Where(f => f.CreatedDate >= query.FromDate.Value);
        if (query.ToDate.HasValue)
            q = q.Where(f => f.CreatedDate <= query.ToDate.Value);

        var totalCount = await q.CountAsync();

        var items = await q
            .OrderByDescending(f => f.CreatedDate)
            .Skip((query.Page - 1) * query.PageSize)
            .Take(query.PageSize)
            .ToListAsync();

        return new PaginatedResponse<Flight>
        {
            Items = items,
            TotalCount = totalCount,
            Page = query.Page,
            PageSize = query.PageSize
        };
    }

    public async Task DeleteFlight(Guid id)
    {
        var flight = await db.Flights.FirstOrDefaultAsync(f => f.Id == id)
            ?? throw new KeyNotFoundException($"Flight {id} not found");

        await db.MapObjects.DeleteAsync(m => m.FlightId == id);
        await db.GpsCorrections.DeleteAsync(g => g.FlightId == id);
        await db.Orthophotos.DeleteAsync(o => o.FlightId == id);

        var waypointIds = await db.Waypoints.Where(w => w.FlightId == id).Select(w => w.Id).ToListAsync();
        if (waypointIds.Count > 0)
        {
            var mediaIds = await db.Media.Where(m => m.WaypointId != null && waypointIds.Contains(m.WaypointId!.Value))
                .Select(m => m.Id).ToListAsync();
            if (mediaIds.Count > 0)
            {
                var annotationIds = await db.Annotations.Where(a => mediaIds.Contains(a.MediaId))
                    .Select(a => a.Id).ToListAsync();
                if (annotationIds.Count > 0)
                    await db.Detections.DeleteAsync(d => annotationIds.Contains(d.AnnotationId));
                await db.Annotations.DeleteAsync(a => mediaIds.Contains(a.MediaId));
            }
            await db.Media.DeleteAsync(m => m.WaypointId != null && waypointIds.Contains(m.WaypointId!.Value));
        }

        await db.Waypoints.DeleteAsync(w => w.FlightId == id);
        await db.Flights.DeleteAsync(f => f.Id == id);
    }
}
