from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
import random
from .names import OrderNames
from .attributeset import AttributeSet
from .engineeringmodel import EngineeringModel
from .worker import Worker
from .offer import Offer
from .event import (
    SpecialOrder,
    SOChangeRequirement,
    SORegularIncrementUpdates,
    SOPrototyp,
)

# in attributeset einbauen?
attributelist = [
    "experience",
    "reliability",
    "python",
    "javascript",
    "cpp",
    "management",
]


# order zum abarbeiten
class Order(models.Model):
    name = models.CharField(default="random order", max_length=20)
    profit = models.IntegerField(default=1000)
    status = models.IntegerField(
        default=0, validators=[MinValueValidator(0), MaxValueValidator(1)]
    )
    maxRounds = models.IntegerField(
        default=5, validators=[MinValueValidator(5), MaxValueValidator(100)]
    )
    requiredWorkload = models.ForeignKey(
        AttributeSet, default=1, on_delete=models.CASCADE
    )
    engineeringModel = models.ForeignKey(
        EngineeringModel, default=1, on_delete=models.DO_NOTHING, blank=True
    )
    assignedWorkers = models.ManyToManyField(Worker, blank=True)
    offers = models.ManyToManyField(
        Offer,
        blank=True,
        related_name="order_offers",
    )
    specialorder = models.ForeignKey(
        SpecialOrder, default=1, on_delete=models.CASCADE, blank=True
    )
    recalculated = models.BooleanField(default=False)

    def __str__(self):
        return self.name

    def getRandomSpecialorder(name):
        randnum = random.randint(1, 3)
        if randnum == 1:
            specialorder = SOPrototyp.createRandom(name)
        elif randnum == 2:
            specialorder = SOChangeRequirement.createRandom(name)
        else:
            specialorder = SORegularIncrementUpdates.createRandom(name)
        return specialorder

    @classmethod
    def createRandom(cls):
        name = random.choice(OrderNames.objects.all()).name
        profit = random.randint(40000, 100000)
        maxRounds = round(random.uniform(0.7, 1.3) * (profit / 10000))
        requiredWorkload = AttributeSet.createRandom(maxRounds * 13)
        engineeringModel = EngineeringModel.objects.all().get(name="ModelChaotic")
        specialorder = cls.getRandomSpecialorder(name)
        order = cls(
            name=name,
            profit=profit,
            maxRounds=maxRounds,
            requiredWorkload=requiredWorkload,
            engineeringModel=engineeringModel,
            specialorder=specialorder,
        )
        order.save()
        return order

    def setStatus(self, status):
        self.status = status
        self.save()

    def assignWorker(self, worker):
        worker.setStatus(2)
        self.assignedWorkers.add(worker)

    def unassignWorker(self, worker):
        worker.setStatus(1)
        self.assignedWorkers.remove(worker)

    def setEngineeringmodel(self, model):
        self.engineeringModel = model
        self.save()

    def decreaseRounds(self):
        self.maxRounds -= 1
        self.save()

    def recalculate(self, company):
        if not self.recalculated:
            self.engineeringModel.recalculate(order=self, company=company)
            print("recalculated")

    def calculateWorkload(self, workersatisfaction):
        self.decreaseRounds()
        finished = False
        for worker in self.assignedWorkers.all():
            if worker.skilledModels == self.engineeringModel:
                if self.engineeringModel.name == "ModelScrum":
                    amplifyer = self.engineeringModel.bonus(worker=worker, order=self)
                else:
                    amplifyer = worker.amplifyer
            else:
                self.engineeringModel.penalty(worker=worker, order=self)
                if self.engineeringModel.name == "ModelScrum":
                    amplifyer = 0.6
                else:
                    amplifyer = 1

            # arbeit an order wenn nicht blockeirt
            if worker.status != 3:
                for attribute in attributelist:
                    newval = getattr(self.requiredWorkload, attribute) - (
                        getattr(worker.attributes, attribute)
                        * amplifyer
                        * (workersatisfaction * 0.01)
                    )
                    setattr(
                        self.requiredWorkload,
                        attribute,
                        max(0, newval),  # 0 setzen wenn negativwert
                    )

                self.requiredWorkload.save()

        # prüfe ob alles abgearbeitet
        finished = True
        for attribute in attributelist:
            if getattr(self.requiredWorkload, attribute) > 0:
                finished = False
                break

        return finished

    def changeWorkload(self, number):
        for attribute in attributelist:
            setattr(
                self.requiredWorkload,
                attribute,
                getattr(self.requiredWorkload, attribute) * number,
            )

        self.requiredWorkload.save()

    def addWorkload(self, number):
        for attribute in attributelist:
            setattr(
                self.requiredWorkload,
                attribute,
                getattr(self.requiredWorkload, attribute) + number,
            )

        self.requiredWorkload.save()

    def finishOrder(self, company):
        company.addProfit(self.profit)
        company.calculateCustomerSatisfaction(30)
        company.save()
        self.cancelOrder()

    def addOffer(self, offer):
        self.offers.add(offer)

    def removeOffer(self, offer):
        self.offers.remove(offer)

    def checkOffer(self, amount):
        if amount > (self.profit * 1.2) or amount < (self.profit * 0.8):
            return False
        else:
            return True

    def calculateBestOffer(self):
        if self.offers.exists():
            # Wenn offers vorhanden
            bestoffer = (0, 0)
            for offer in self.offers.all():
                x = float(offer.company.customersatisfaction) / float(offer.amount)
                if x >= bestoffer[0]:
                    bestoffer = (x, offer)
                    self.setProfit(offer.amount)
            return bestoffer[1]
        else:
            # wenn keine offers
            return

    def cancelOrder(self):
        for worker in self.assignedWorkers.all():
            self.unassignWorker(worker)
        self.save()

    def setProfit(self, amount):
        self.profit = amount
        self.save()
