using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ContactManagementSystem.Models;

namespace ContactManagementSystem.Data.Repositories
{
    /// <summary>
    /// Implementation of IContactNoteRepository using Entity Framework Core
    /// Demonstrates: Repository Pattern for related entities, LINQ with navigation properties
    /// </summary>
    public class ContactNoteRepository : IContactNoteRepository
    {
        private readonly ApplicationDbContext _context;
        private bool _disposed = false;

        #region Constructor

        public ContactNoteRepository()
        {
            _context = new ApplicationDbContext();
        }

        public ContactNoteRepository(ApplicationDbContext context)
        {
            _context = context ?? throw new ArgumentNullException(nameof(context));
        }

        #endregion

        #region CRUD Operations

        public async Task<List<ContactNote>> GetAllAsync()
        {
            return await _context.ContactNotes
                .Include(n => n.Contact)  // Include related contact
                .OrderByDescending(n => n.DateCreated)
                .ToListAsync();
        }

        public async Task<ContactNote> GetByIdAsync(int id)
        {
            return await _context.ContactNotes
                .Include(n => n.Contact)
                .FirstOrDefaultAsync(n => n.Id == id);
        }

        public async Task<ContactNote> AddAsync(ContactNote note)
        {
            if (note == null)
                throw new ArgumentNullException(nameof(note));

            // Validate ContactId exists
            var contactExists = await _context.Contacts.AnyAsync(c => c.Id == note.ContactId);
            if (!contactExists)
                throw new InvalidOperationException($"Contact with ID {note.ContactId} does not exist.");

            note.DateCreated = DateTime.Now;

            await _context.ContactNotes.AddAsync(note);
            await _context.SaveChangesAsync();

            return note;
        }

        public async Task<bool> UpdateAsync(ContactNote note)
        {
            if (note == null)
                throw new ArgumentNullException(nameof(note));

            var existing = await _context.ContactNotes.FindAsync(note.Id);
            if (existing == null)
                return false;

            _context.Entry(existing).CurrentValues.SetValues(note);
            await _context.SaveChangesAsync();

            return true;
        }

        public async Task<bool> DeleteAsync(int id)
        {
            var note = await _context.ContactNotes.FindAsync(id);
            if (note == null)
                return false;

            _context.ContactNotes.Remove(note);
            await _context.SaveChangesAsync();

            return true;
        }

        #endregion

        #region Query Operations

        public async Task<List<ContactNote>> GetByContactIdAsync(int contactId)
        {
            return await _context.ContactNotes
                .Where(n => n.ContactId == contactId)
                .OrderByDescending(n => n.DateCreated)
                .ToListAsync();
        }

        public async Task<List<ContactNote>> GetByCategoryAsync(string category)
        {
            if (string.IsNullOrWhiteSpace(category))
                return new List<ContactNote>();

            return await _context.ContactNotes
                .Include(n => n.Contact)
                .Where(n => n.Category != null && n.Category.ToLower() == category.ToLower())
                .OrderByDescending(n => n.DateCreated)
                .ToListAsync();
        }

        public async Task<List<ContactNote>> SearchAsync(string searchTerm)
        {
            if (string.IsNullOrWhiteSpace(searchTerm))
                return new List<ContactNote>();

            searchTerm = searchTerm.ToLower();

            return await _context.ContactNotes
                .Include(n => n.Contact)
                .Where(n => n.Content.ToLower().Contains(searchTerm) ||
                           (n.Category != null && n.Category.ToLower().Contains(searchTerm)))
                .OrderByDescending(n => n.DateCreated)
                .ToListAsync();
        }

        public async Task<List<ContactNote>> GetRecentAsync(int days)
        {
            var cutoffDate = DateTime.Now.AddDays(-days);

            return await _context.ContactNotes
                .Include(n => n.Contact)
                .Where(n => n.DateCreated >= cutoffDate)
                .OrderByDescending(n => n.DateCreated)
                .ToListAsync();
        }

        public async Task<List<ContactNote>> GetNotesAfterDateAsync(DateTime date)
        {
            return await _context.ContactNotes
                .Include(n => n.Contact)
                .Where(n => n.DateCreated >= date)
                .OrderByDescending(n => n.DateCreated)
                .ToListAsync();
        }

        #endregion

        #region Utility Operations

        public async Task<int> GetCountByContactAsync(int contactId)
        {
            return await _context.ContactNotes
                .Where(n => n.ContactId == contactId) // LINQ
                .CountAsync(); // EF CORE Call to Action
        }

        public async Task<int> GetCountAsync()
        {
            return await _context.ContactNotes.CountAsync();
        }

        #endregion

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _context?.Dispose();
                }
                _disposed = true;
            }
        }

        #endregion
    }
}
