using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Org.BouncyCastle.Tls;
using Silk_Road_Api.Extensions;
using Silk_Road_Api.Models;
using Silk_Road_Api.Repositories;
using Silk_Road_Api.Utils;
using Silk_Road_Api.ViewModels;

namespace Silk_Road_Api.Services;

public class DrugService : IDrugService
{
    private readonly IDrugRepository _drugRepository;
    private readonly IManufacturerRepository _manufacturerRepository;
    private readonly IMapper _mapper;

    public DrugService(IDrugRepository drugRepository, IManufacturerRepository manufacturerRepository, IMapper mapper)
    {
        _drugRepository = drugRepository;
        _manufacturerRepository = manufacturerRepository;
        _mapper = mapper;
    }

    public async Task<DrugViewModel> GetDrug(ControllerBase controller, int id)
    {
        var user = controller.HttpContext.Session.Get<User>("user");
        if(user == null)
        {
            throw new Exception("User not logged in");
        }
        var drug = await _drugRepository.GetDrug(id);
        if (drug == null)
            throw new Exception("Drug not found");
        if (user.id != Constants.Admin && drug.manufacturerId != user.companyid)
            throw new Exception("Drug does not belong to user");
        var manufacturer = await _manufacturerRepository.GetManufacturer(drug.manufacturerId);
        if (manufacturer == null)
            throw new Exception("Manufacturer not found");
        var viewModel = _mapper.Map<DrugViewModel>(drug);
        viewModel.manufacturer = manufacturer.name;
        return viewModel;

    }

    public async Task<SearchOutput<DrugViewModel>> GetDrugs(ControllerBase controller, SearchInput input)
    {
        var user = controller.HttpContext.Session.Get<User>("user");
        if (user == null)
        {
            throw new Exception("User not logged in");
        }
        var output = new SearchOutput<DrugViewModel>
        {
            Page = input.Page,
            PageSize = input.PageSize
        };
        var manufacturer = _manufacturerRepository.GetManufacturers();
        if (user.id != Constants.Admin)
        {
            manufacturer = manufacturer.Where(m => m.id == user.companyid);
        }
        var drugs = _drugRepository.GetDrugs();
        var drugViewModels = drugs.Join(manufacturer, d => d.manufacturerId, m => m.id, (d, m) => new DrugViewModel
        {
            id = d.id,
            name = d.name,
            price = d.price,
            stock = d.stock,
            manufacturer = m.name,
            description = d.description,
            sideEffects = d.sideEffects,
            interactions = d.interactions,
            dosage = d.dosage,
            contraindications = d.contraindications,
            warnings = d.warnings,
            storage = d.storage,
            pregnancy = d.pregnancy,
            breastfeeding = d.breastfeeding,
            overdose = d.overdose
        });
        output.Total = await _drugRepository.ExecuteCount(drugs);
        drugViewModels = input.Sorting switch
        {
            "name" => drugViewModels.OrderBy(d => d.name),
            "price" => drugViewModels.OrderBy(d => d.price),
            "manufacturer" => drugViewModels.OrderBy(d => d.manufacturer),
            "name_desc" => drugViewModels.OrderByDescending(d => d.name),
            "price_desc" => drugViewModels.OrderByDescending(d => d.price),
            "manufacturer_desc" => drugViewModels.OrderByDescending(d => d.manufacturer),
            _ => drugViewModels.OrderBy(d => d.id),
        };
        drugViewModels = drugViewModels.Skip(input.PageSize * (input.Page - 1)).Take(input.PageSize);
        output.Items = drugViewModels.ToList();
        return output;
    }


    public async Task<bool> AddDrug(ControllerBase controller, DrugViewModel drugViewModel)
    {
        var user = controller.HttpContext.Session.Get<User>("user");
        if (user == null)
        {
            return false;
        }
        try
        {
            var manufacturerId = await _manufacturerRepository.GetManufacturerByName(drugViewModel.manufacturer);
            if (manufacturerId == 0)
                throw new Exception("Manufacturer not found");
            if (user.id != Constants.Admin && user.companyid != manufacturerId)
                throw new Exception("Illegal operation");
            var drug = _mapper.Map<Drug>(drugViewModel);
            drug.manufacturerId = manufacturerId;
            await _drugRepository.AddAsync(drug);
            return await _drugRepository.SaveChangesAsync();
        }
        catch (Exception)
        {
            return false;
        }
    }

    public async Task<bool> UpdateDrug(ControllerBase controller, DrugViewModel drugViewModel)
    {
        var user = controller.HttpContext.Session.Get<User>("user");
        if (user == null)
        {
            return false;
        }
        try
        {
            var manufacturerId = await _manufacturerRepository.GetManufacturerByName(drugViewModel.manufacturer);
            if (manufacturerId == 0)
                throw new Exception("Manufacturer not found");
            if (user.id != Constants.Admin && user.companyid != manufacturerId)
                throw new Exception("Illegal operation");
            var drug = _mapper.Map<Drug>(drugViewModel);
            drug.manufacturerId = manufacturerId;
            _drugRepository.Update(drug);
            return await _drugRepository.SaveChangesAsync();
        }
        catch (Exception)
        {
            return false;
        }
    }

    public async Task<bool> DeleteDrug(ControllerBase controller, int id)
    {
        var user = controller.HttpContext.Session.Get<User>("user");
        if (user == null)
        {
            return false;
        }
        try
        {
            var drug = await _drugRepository.GetDrug(id);
            if (user.id != Constants.Admin && user.companyid != drug.manufacturerId)
                throw new Exception("Illegal operation");
            _drugRepository.Delete(drug);
            return await _drugRepository.SaveChangesAsync();
        }
        catch (Exception)
        {
            return false;
        }
    }

    public void GenerateDrugs()
    {
        _drugRepository.GenerateDrugs();
    }

    public async Task<List<ChartViewModel>> GetChartData()
    {
        return (await _drugRepository.GetChartData()).Join(_manufacturerRepository.GetManufacturers(), d => d.ManufacturerId, m => m.id, (d, m) => new ChartViewModel
        {
            Manufacturer = m.name,
            Count = d.Count
        }).ToList();
    }
}
