using CMSMicroservice.Protobuf.Protos.City;
using CMSMicroservice.WebApi.Common.Services;
using CMSMicroservice.Application.CityCQ.Queries.GetAllCitiesByFilter;
using CMSMicroservice.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;
using Google.Protobuf.WellKnownTypes;
using System.Linq;

namespace CMSMicroservice.WebApi.Services;

public class CityService : CityContract.CityContractBase
{
    private readonly IDispatchRequestToCQRS _dispatchRequestToCQRS;
    private readonly IApplicationDbContext _context;

    public CityService(IDispatchRequestToCQRS dispatchRequestToCQRS, IApplicationDbContext context)
    {
        _dispatchRequestToCQRS = dispatchRequestToCQRS;
        _context = context;
    }

    public override async Task<GetAllCitiesByFilterResponse> GetAllCitiesByFilter(
        GetAllCitiesByFilterRequest request, 
        ServerCallContext context)
    {
        return await _dispatchRequestToCQRS.Handle<
            GetAllCitiesByFilterRequest, 
            GetAllCitiesByFilterQuery, 
            GetAllCitiesByFilterResponse>(request, context);
    }

    #region Customer Methods

    public override async Task<GetCitiesForCustomerResponse> GetCitiesForCustomer(
        GetCitiesForCustomerRequest request, ServerCallContext context)
    {
        var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1;
        var pageSize = request.PageSize > 0 ? request.PageSize : 50;
        
        var query = _context.Cities
            .Include(c => c.State)
            .Where(c => !c.IsDeleted);
        
        if (request.StateId != null)
            query = query.Where(c => c.StateId == request.StateId.Value);
        
        if (!string.IsNullOrWhiteSpace(request.SearchTerm))
            query = query.Where(c => c.Name.Contains(request.SearchTerm) || c.Native.Contains(request.SearchTerm));
        
        var totalCount = await query.CountAsync(context.CancellationToken);
        
        var cities = await query
            .OrderBy(c => c.Native)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .Select(c => new CityDto
            {
                Id = c.Id,
                ExternalId = c.ExternalId,
                Name = c.Name,
                Native = c.Native,
                Latitude = c.Latitude ?? string.Empty,
                Longitude = c.Longitude ?? string.Empty,
                StateId = c.StateId,
                StateName = c.State != null ? c.State.Name : string.Empty,
                StateNative = c.State != null ? c.State.Native : string.Empty
            })
            .ToListAsync(context.CancellationToken);
        
        return new GetCitiesForCustomerResponse
        {
            Cities = { cities },
            MetaData = new CMSMicroservice.Protobuf.Protos.City.MetaData
            {
                CurrentPage = pageNumber,
                PageSize = pageSize,
                TotalCount = totalCount,
                TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
                HasNext = pageNumber * pageSize < totalCount,
                HasPrevious = pageNumber > 1
            }
        };
    }

    public override async Task<GetCityByIdForCustomerResponse> GetCityByIdForCustomer(
        GetCityByIdForCustomerRequest request, ServerCallContext context)
    {
        var city = await _context.Cities
            .Include(c => c.State)
            .Where(c => c.Id == request.Id && !c.IsDeleted)
            .Select(c => new CityDto
            {
                Id = c.Id,
                ExternalId = c.ExternalId,
                Name = c.Name,
                Native = c.Native,
                Latitude = c.Latitude ?? string.Empty,
                Longitude = c.Longitude ?? string.Empty,
                StateId = c.StateId,
                StateName = c.State != null ? c.State.Name : string.Empty,
                StateNative = c.State != null ? c.State.Native : string.Empty
            })
            .FirstOrDefaultAsync(context.CancellationToken);
        
        if (city == null)
            throw new RpcException(new Status(StatusCode.NotFound, "شهر یافت نشد"));
        
        return new GetCityByIdForCustomerResponse { City = city };
    }

    public override async Task<GetCitiesByStateForCustomerResponse> GetCitiesByStateForCustomer(
        GetCitiesByStateForCustomerRequest request, ServerCallContext context)
    {
        var pageNumber = request.PageNumber > 0 ? request.PageNumber : 1;
        var pageSize = request.PageSize > 0 ? request.PageSize : 100;
        
        var query = _context.Cities
            .Include(c => c.State)
            .Where(c => c.StateId == request.StateId && !c.IsDeleted);
        
        var totalCount = await query.CountAsync(context.CancellationToken);
        
        var cities = await query
            .OrderBy(c => c.Native)
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .Select(c => new CityDto
            {
                Id = c.Id,
                ExternalId = c.ExternalId,
                Name = c.Name,
                Native = c.Native,
                Latitude = c.Latitude ?? string.Empty,
                Longitude = c.Longitude ?? string.Empty,
                StateId = c.StateId,
                StateName = c.State != null ? c.State.Name : string.Empty,
                StateNative = c.State != null ? c.State.Native : string.Empty
            })
            .ToListAsync(context.CancellationToken);
        
        return new GetCitiesByStateForCustomerResponse
        {
            Cities = { cities },
            MetaData = new CMSMicroservice.Protobuf.Protos.City.MetaData
            {
                CurrentPage = pageNumber,
                PageSize = pageSize,
                TotalCount = totalCount,
                TotalPage = (int)Math.Ceiling(totalCount / (double)pageSize),
                HasNext = pageNumber * pageSize < totalCount,
                HasPrevious = pageNumber > 1
            }
        };
    }

    // Admin Methods
    public override async Task<CreateCityResponse> CreateCity(
        CreateCityRequest request, ServerCallContext context)
    {
        var city = new Domain.Entities.Geography.City
        {
            ExternalId = request.ExternalId,
            Name = request.Name,
            Native = request.Native,
            Latitude = request.Latitude ?? string.Empty,
            Longitude = request.Longitude ?? string.Empty,
            StateId = request.StateId
        };
        
        _context.Cities.Add(city);
        await _context.SaveChangesAsync(context.CancellationToken);
        
        return new CreateCityResponse
        {
            Id = city.Id,
            Message = "شهر با موفقیت ایجاد شد"
        };
    }

    #region [ARCHIVED] UpdateCity & DeleteCity — شهرها seed data هستند، ویرایش/حذف دستی باعث خرابی آدرس‌ها می‌شود
    [Obsolete("ARCHIVED: Cities are seed data — manual edit/delete breaks user addresses")]
    public override async Task<Empty> UpdateCity(
        UpdateCityRequest request, ServerCallContext context)
    {
        var city = await _context.Cities.FindAsync(new object[] { request.Id }, context.CancellationToken);
        if (city == null)
            throw new RpcException(new Status(StatusCode.NotFound, "شهر یافت نشد"));
        
        city.ExternalId = request.ExternalId;
        city.Name = request.Name;
        city.Native = request.Native;
        if (request.Latitude != null) city.Latitude = request.Latitude;
        if (request.Longitude != null) city.Longitude = request.Longitude;
        city.StateId = request.StateId;
        
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Empty();
    }

    [Obsolete("ARCHIVED: Cities are seed data — manual delete breaks user addresses")]
    public override async Task<Empty> DeleteCity(
        DeleteCityRequest request, ServerCallContext context)
    {
        var city = await _context.Cities.FindAsync(new object[] { request.Id }, context.CancellationToken);
        if (city == null)
            throw new RpcException(new Status(StatusCode.NotFound, "شهر یافت نشد"));
        
        city.IsDeleted = true;
        await _context.SaveChangesAsync(context.CancellationToken);
        return new Empty();
    }
    #endregion

    #endregion
}
