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 BooksController : ControllerBase
    {
        private readonly LibraryDbContext _context;

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

        // GET: api/books
        [HttpGet]
        public async Task<ActionResult<IEnumerable<BookDto>>> GetBooks([FromQuery] BookFilterParams filters)
        {
            var query = _context.Books
                .Include(b => b.Author)
                .AsQueryable();

            // Apply filters
            if (!string.IsNullOrWhiteSpace(filters.Title))
                query = query.Where(b => b.Title.Contains(filters.Title));

            if (!string.IsNullOrWhiteSpace(filters.Genre))
                query = query.Where(b => b.Genre != null && b.Genre.Contains(filters.Genre));

            if (filters.AuthorId.HasValue)
                query = query.Where(b => b.AuthorId == filters.AuthorId.Value);

            if (filters.IsAvailable.HasValue)
            {
                if (filters.IsAvailable.Value)
                    query = query.Where(b => b.AvailableCopies > 0);
                else
                    query = query.Where(b => b.AvailableCopies == 0);
            }

            if (filters.PublishedYearFrom.HasValue)
                query = query.Where(b => b.PublishedYear >= filters.PublishedYearFrom.Value);

            if (filters.PublishedYearTo.HasValue)
                query = query.Where(b => b.PublishedYear <= filters.PublishedYearTo.Value);

            if (filters.PriceMax.HasValue)
                query = query.Where(b => b.Price <= filters.PriceMax.Value);

            var books = await query
                .OrderBy(b => b.Title)
                .Select(b => new BookDto
                {
                    Id = b.Id,
                    Title = b.Title,
                    ISBN = b.ISBN,
                    Genre = b.Genre,
                    PublishedYear = b.PublishedYear,
                    Price = b.Price,
                    TotalCopies = b.TotalCopies,
                    AvailableCopies = b.AvailableCopies,
                    Description = b.Description,
                    CreatedAt = b.CreatedAt,
                    AuthorId = b.AuthorId,
                    AuthorFullName = b.Author.FirstName + " " + b.Author.LastName
                })
                .ToListAsync();

            return Ok(books);
        }

        // GET: api/books/5
        [HttpGet("{id}")]
        public async Task<ActionResult<BookDto>> GetBook(int id)
        {
            var book = await _context.Books
                .Include(b => b.Author)
                .FirstOrDefaultAsync(b => b.Id == id);

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

            return Ok(MapToDto(book));
        }

        // POST: api/books
        [HttpPost]
        public async Task<ActionResult<BookDto>> CreateBook([FromBody] BookCreateDto dto)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            // Validate author exists
            var authorExists = await _context.Authors.AnyAsync(a => a.Id == dto.AuthorId);
            if (!authorExists)
                return BadRequest(new { message = $"Author with ID {dto.AuthorId} does not exist." });

            // Validate ISBN uniqueness
            var isbnExists = await _context.Books.AnyAsync(b => b.ISBN == dto.ISBN);
            if (isbnExists)
                return Conflict(new { message = $"A book with ISBN '{dto.ISBN}' already exists." });

            var book = new Book
            {
                Title = dto.Title,
                ISBN = dto.ISBN,
                Genre = dto.Genre,
                PublishedYear = dto.PublishedYear,
                Price = dto.Price,
                TotalCopies = dto.TotalCopies,
                AvailableCopies = dto.TotalCopies, // all copies available on creation
                Description = dto.Description,
                AuthorId = dto.AuthorId,
                CreatedAt = DateTime.UtcNow
            };

            _context.Books.Add(book);
            await _context.SaveChangesAsync();

            // Reload with Author for the response
            await _context.Entry(book).Reference(b => b.Author).LoadAsync();

            return CreatedAtAction(nameof(GetBook), new { id = book.Id }, MapToDto(book));
        }

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

            var book = await _context.Books
                .Include(b => b.Author)
                .FirstOrDefaultAsync(b => b.Id == id);

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

            // Validate author exists
            var authorExists = await _context.Authors.AnyAsync(a => a.Id == dto.AuthorId);
            if (!authorExists)
                return BadRequest(new { message = $"Author with ID {dto.AuthorId} does not exist." });

            // Validate ISBN uniqueness (exclude this book)
            var isbnConflict = await _context.Books
                .AnyAsync(b => b.ISBN == dto.ISBN && b.Id != id);
            if (isbnConflict)
                return Conflict(new { message = $"Another book with ISBN '{dto.ISBN}' already exists." });

            // Cannot set AvailableCopies higher than TotalCopies
            if (dto.AvailableCopies > dto.TotalCopies)
                return BadRequest(new { message = "Available copies cannot exceed total copies." });

            book.Title = dto.Title;
            book.ISBN = dto.ISBN;
            book.Genre = dto.Genre;
            book.PublishedYear = dto.PublishedYear;
            book.Price = dto.Price;
            book.TotalCopies = dto.TotalCopies;
            book.AvailableCopies = dto.AvailableCopies;
            book.Description = dto.Description;
            book.AuthorId = dto.AuthorId;

            await _context.SaveChangesAsync();

            // Reload Author if it changed
            await _context.Entry(book).Reference(b => b.Author).LoadAsync();

            return Ok(MapToDto(book));
        }

        // DELETE: api/books/5
        [HttpDelete("{id}")]
        public async Task<IActionResult> DeleteBook(int id)
        {
            var book = await _context.Books
                .Include(b => b.BorrowRecords)
                .FirstOrDefaultAsync(b => b.Id == id);

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

            var activeBorrows = book.BorrowRecords
                .Count(br => br.Status == BorrowStatus.Active || br.Status == BorrowStatus.Overdue);

            if (activeBorrows > 0)
                return Conflict(new
                {
                    message = $"Cannot delete '{book.Title}' because it has {activeBorrows} active/overdue borrow record(s)."
                });

            _context.Books.Remove(book);
            await _context.SaveChangesAsync();

            return NoContent();
        }

        // ── Helper ────────────────────────────────────────────────────────────

        private static BookDto MapToDto(Book book) => new BookDto
        {
            Id = book.Id,
            Title = book.Title,
            ISBN = book.ISBN,
            Genre = book.Genre,
            PublishedYear = book.PublishedYear,
            Price = book.Price,
            TotalCopies = book.TotalCopies,
            AvailableCopies = book.AvailableCopies,
            Description = book.Description,
            CreatedAt = book.CreatedAt,
            AuthorId = book.AuthorId,
            AuthorFullName = book.Author.FirstName + " " + book.Author.LastName
        };
    }
}
