using LibraryAPI.Data;
using LibraryAPI.DTOs;
using LibraryAPI.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace LibraryAPI.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class AuthorsController : ControllerBase
    {
        private readonly LibraryDbContext _context;

        public AuthorsController(LibraryDbContext context)
        {
            _context = context;
        }

        // GET: api/authors
        [HttpGet]
        public async Task<ActionResult<IEnumerable<AuthorDto>>> GetAuthors(
            [FromQuery] string? name,
            [FromQuery] string? nationality)
        {
            var query = _context.Authors
                .Include(a => a.Books)
                .AsQueryable();

            if (!string.IsNullOrWhiteSpace(name))
                query = query.Where(a =>
                    a.FirstName.Contains(name) || a.LastName.Contains(name));

            if (!string.IsNullOrWhiteSpace(nationality))
                query = query.Where(a => a.Nationality != null &&
                    a.Nationality.Contains(nationality));

            var authors = await query
                .OrderBy(a => a.LastName)
                .ThenBy(a => a.FirstName)
                .Select(a => new AuthorDto
                {
                    Id = a.Id,
                    FirstName = a.FirstName,
                    LastName = a.LastName,
                    Biography = a.Biography,
                    Nationality = a.Nationality,
                    CreatedAt = a.CreatedAt,
                    BookCount = a.Books.Count
                })
                .ToListAsync();

            return Ok(authors);
        }

        // GET: api/authors/5
        [HttpGet("{id}")]
        public async Task<ActionResult<AuthorDto>> GetAuthor(int id)
        {
            var author = await _context.Authors
                .Include(a => a.Books)
                .FirstOrDefaultAsync(a => a.Id == id);

            if (author == null)
                return NotFound(new { message = $"Author with ID {id} not found." });

            return Ok(new AuthorDto
            {
                Id = author.Id,
                FirstName = author.FirstName,
                LastName = author.LastName,
                Biography = author.Biography,
                Nationality = author.Nationality,
                CreatedAt = author.CreatedAt,
                BookCount = author.Books.Count
            });
        }

        // POST: api/authors
        [HttpPost]
        public async Task<ActionResult<AuthorDto>> CreateAuthor([FromBody] AuthorCreateDto dto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            var author = new Author
            {
                FirstName = dto.FirstName,
                LastName = dto.LastName,
                Biography = dto.Biography,
                Nationality = dto.Nationality,
                CreatedAt = DateTime.UtcNow
            };

            _context.Authors.Add(author);
            await _context.SaveChangesAsync();

            var result = new AuthorDto
            {
                Id = author.Id,
                FirstName = author.FirstName,
                LastName = author.LastName,
                Biography = author.Biography,
                Nationality = author.Nationality,
                CreatedAt = author.CreatedAt,
                BookCount = 0
            };

            return CreatedAtAction(nameof(GetAuthor), new { id = author.Id }, result);
        }

        // PUT: api/authors/5
        [HttpPut("{id}")]
        public async Task<ActionResult<AuthorDto>> UpdateAuthor(int id, [FromBody] AuthorUpdateDto dto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            var author = await _context.Authors
                .Include(a => a.Books)
                .FirstOrDefaultAsync(a => a.Id == id);

            if (author == null)
                return NotFound(new { message = $"Author with ID {id} not found." });

            author.FirstName = dto.FirstName;
            author.LastName = dto.LastName;
            author.Biography = dto.Biography;
            author.Nationality = dto.Nationality;

            await _context.SaveChangesAsync();

            return Ok(new AuthorDto
            {
                Id = author.Id,
                FirstName = author.FirstName,
                LastName = author.LastName,
                Biography = author.Biography,
                Nationality = author.Nationality,
                CreatedAt = author.CreatedAt,
                BookCount = author.Books.Count
            });
        }

        // DELETE: api/authors/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteAuthor(int id)
        {
            var author = await _context.Authors
                .Include(a => a.Books)
                .FirstOrDefaultAsync(a => a.Id == id);

            if (author == null)
                return NotFound(new { message = $"Author with ID {id} not found." });

            if (author.Books.Any())
                return Conflict(new
                {
                    message = $"Cannot delete author '{author.FirstName} {author.LastName}' because they have {author.Books.Count} book(s) in the system. Remove those books first."
                });

            _context.Authors.Remove(author);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        // ── Complex Feature #1 ────────────────────────────────────────────────
        // GET: api/authors/top-borrowed?topN=5
        // Returns the authors whose books have been borrowed the most (joins Authors + Books + BorrowRecords)
        [HttpGet("top-borrowed")]
        public async Task<ActionResult<IEnumerable<TopBorrowedAuthorDto>>> GetTopBorrowedAuthors(
            [FromQuery] int topN = 5)
        {
            if (topN < 1 || topN > 100)
                return BadRequest(new { message = "topN must be between 1 and 100." });

            var result = await _context.Authors
                .Include(a => a.Books)
                    .ThenInclude(b => b.BorrowRecords)
                .Select(a => new TopBorrowedAuthorDto
                {
                    AuthorId = a.Id,
                    FullName = a.FirstName + " " + a.LastName,
                    Nationality = a.Nationality,
                    TotalBorrows = a.Books.SelectMany(b => b.BorrowRecords).Count(),
                    UniqueBooks = a.Books.Count(b => b.BorrowRecords.Any()),
                    LastBorrowDate = a.Books
                        .SelectMany(b => b.BorrowRecords)
                        .Select(br => (DateTime?)br.BorrowDate)
                        .Max()
                })
                .Where(a => a.TotalBorrows > 0)
                .OrderByDescending(a => a.TotalBorrows)
                .Take(topN)
                .ToListAsync();

            return Ok(result);
        }
    }
}
