using Employees;
using Offers;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Managers
{
    public class EmployeeManager : MonoBehaviour
    {
        [SerializeField] private IconConfig animatorIcons;

        [Header("Generation")]
        [SerializeField] private int workersForHire;
        [SerializeField] private int minSalary;
        [SerializeField] private int maxSalary;
        [SerializeField] private int salaryDivider;
        [SerializeField] private int minTimeOnEvent;
        [SerializeField] private int maxTimeOnEvent;
        [SerializeField] private int timeDivider;
        [SerializeField] private int minEventsPerWeek;
        [SerializeField] private int maxEventsPerWeek;

        protected List<Employee> employees = new List<Employee>();

        List<Employee> freeEmployees = new List<Employee>();

        List<Employee> employeesForHire = new List<Employee>();

        DataManager dataManager;

        public List<Employee> OurEmployees => employees;
        public List<Employee> HireEmployees => employeesForHire;

        private void Start()
        {
            dataManager = DataManager.Instance;

            dataManager.SubscribeOnLoad(delegate
            {
                GenerateEmployeesFromSave();
            });

            for(int i = 0; i < workersForHire; i++)
                employeesForHire.Add(CreateEmployee());
        }

        public int GetEmployeesCount() => employees.Count;

        public void BookEmployees(int count, DateTime eventStart, DateTime eventEnd)
        {
            freeEmployees.Shuffle();

            for (int i = 0; i < count; i++)
                freeEmployees[i].Book(eventStart, eventEnd);
        }

        public bool IsFreeEmployees(int count, DateTime eventStart, DateTime eventEnd)
        {
            freeEmployees = new List<Employee>();

            foreach (var employee in employees)
                if (employee.CanBook(eventStart, eventEnd))
                    freeEmployees.Add(employee);

            return freeEmployees.Count >= count;
        }

        public void HireEmployee(Employee worker)
        {
            employees.Add(worker);
            employeesForHire.Remove(worker);
            dataManager.Employees.Add(worker.Data);
        }

        public void ReleaseOurEmployee(Employee worker)
        {
            employees.Remove(worker);
            dataManager.Employees.Remove(worker.Data);
        }

        public void ReleaseHireEmployee(Employee worker)
        {
            employeesForHire.Remove(worker);
            employeesForHire.Add(CreateEmployee());
        }

        public void UpgradeWorkerTime(Employee worker)
        {
            worker.Data.maxTimeOnEvent += 30;
            dataManager.Budget -= 300;
        }

        public void UpgradeWorkerEvents(Employee worker)
        {
            worker.Data.eventsPerWeek += 1;
            dataManager.Budget -= 200;
        }

        private Employee CreateEmployee()
        {
            var employee = new Employee();
            var data = GenerateRandomEmployeeData();
            employee.SetData(data, animatorIcons.GetSpriteByID(data.iconID));
            return employee;
        }

        private Employee CreateEmployee(EmployeeData data)
        {
            var employee = new Employee();
            employee.SetData(data, animatorIcons.GetSpriteByID(data.iconID));
            return employee;
        }

        public EmployeeData GenerateRandomEmployeeData()
        {
            var newEmployeeData = new EmployeeData();
            newEmployeeData.ID = Guid.NewGuid();

            var person = Peoples.NamesGenerator.GenerateBasePeopleStats();
            newEmployeeData.fullname = person.name + " " + person.surname;

            var icon = animatorIcons.GetRandomIcon(person.sex);
            newEmployeeData.iconID = icon.id;

            var times = GetRange(minTimeOnEvent, maxTimeOnEvent, timeDivider);

            newEmployeeData.maxTimeOnEvent = times[UnityEngine.Random.Range(0, times.Count - 1)];

            newEmployeeData.eventsPerWeek = UnityEngine.Random.Range(minEventsPerWeek, maxEventsPerWeek);

            newEmployeeData.salary = CalculateSalary(newEmployeeData);

            return newEmployeeData;
        }

        public List<EmployeeData> GenerateRandomEmployeeDatas(int count)
        {
            var list = new List<EmployeeData>();

            for (int i = 0; i < count; i++)
                list.Add(GenerateRandomEmployeeData());

            return list;
        }

        public void GenerateEmployeesFromSave()
        {
            employees.Clear();

            var allEmployees = dataManager.Employees;

            foreach (var employeeData in allEmployees)
                employees.Add(CreateEmployee(employeeData));
        }

        private List<int> GetRange(int start, int end, int divider)
        {
            var range = new List<int>();
            int curr = start;
            while (curr <= end)
            {
                range.Add(curr);
                curr += divider;
            }

            return range;
        }

        private int CalculateSalary(EmployeeData data)
        {
            var salaries = GetRange(minSalary, maxSalary, salaryDivider);

            var normalizedTimeOnEvent = NormalizeValue(data.maxTimeOnEvent, minTimeOnEvent, maxTimeOnEvent);
            var normalizedEventsPerWeek = NormalizeValue(data.eventsPerWeek, minEventsPerWeek, maxEventsPerWeek);

            float coef = normalizedTimeOnEvent + normalizedEventsPerWeek;
            coef = NormalizeValue(coef, 0f, 2f);
            int finalSalaryIndex = Mathf.Clamp(Mathf.CeilToInt(salaries.Count * coef) - 1, 0, salaries.Count - 1);
            return salaries[finalSalaryIndex];
        }

        private float NormalizeValue(float value, float min, float max) => (value - min) / (max - min);

        public void UnBookEmployee(Offer offer)
        {
            OurEmployees.ForEach(x => x.UnBook(offer.StartTime));
        }
    }
}