from decimal import Decimal

from django.db import models
from django.template.defaultfilters import date
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from apps.common.models import SharedObject, SharedObjectManager
from apps.currencies.utils.convert import convert, get_exchange_rate


class DCAStrategy(SharedObject):
    name = models.CharField(max_length=255, verbose_name=_("Name"))
    target_currency = models.ForeignKey(
        "currencies.Currency",
        verbose_name=_("Target Currency"),
        on_delete=models.PROTECT,
        related_name="dca_target_strategies",
    )
    payment_currency = models.ForeignKey(
        "currencies.Currency",
        verbose_name=_("Payment Currency"),
        on_delete=models.PROTECT,
        related_name="dca_payment_strategies",
    )
    notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = SharedObjectManager()
    all_objects = models.Manager()  # Unfiltered manager

    class Meta:
        verbose_name = _("DCA Strategy")
        verbose_name_plural = _("DCA Strategies")
        ordering = ["-created_at"]

    def __str__(self):
        return self.name

    def total_invested(self):
        return sum(entry.amount_paid for entry in self.entries.all())

    def total_received(self):
        return sum(entry.amount_received for entry in self.entries.all())

    def average_entry_price(self):
        total_invested = self.total_invested()
        total_received = self.total_received()
        if total_received:
            return total_invested / total_received
        return Decimal("0")

    def total_entries(self):
        return self.entries.count()

    def current_total_value(self):
        """Calculate current total value of all entries"""
        return sum(entry.current_value() for entry in self.entries.all())

    def total_profit_loss(self):
        """Calculate total P/L in payment currency"""
        return self.current_total_value() - self.total_invested()

    def total_profit_loss_percentage(self):
        """Calculate total P/L percentage"""
        total_invested = self.total_invested()
        if total_invested:
            return (self.total_profit_loss() / total_invested) * 100
        return Decimal("0")

    def investment_frequency_data(self):
        def _empty_frequency_data():
            return {
                "intervals_line": [],
                "labels": [],
            }

        entries = self.entries.order_by("date")

        if entries.count() < 2:
            return _empty_frequency_data()

        dates = list(entries.values_list("date", flat=True))
        intervals = [(dates[i + 1] - dates[i]).days for i in range(len(dates) - 1)]

        # Create data points for the intervals chart
        labels = []
        intervals_line = []

        for i in range(len(dates) - 1):
            labels.append(
                f"{date(dates[i], 'SHORT_DATE_FORMAT')} → {date(dates[i + 1], 'SHORT_DATE_FORMAT')}"
            )
            intervals_line.append(intervals[i])

        return {
            "intervals_line": intervals_line,
            "labels": labels,
        }

    def price_comparison_data(self):
        entries = self.entries.order_by("date")

        if entries.count() < 1:
            return {
                "labels": [],
                "entry_prices": [],
                "current_prices": [],
                "amounts_bought": [],
            }

        labels = []
        entry_prices = []
        current_prices = []
        amounts_bought = []

        for entry in entries:
            # Entry price calculation
            entry_price = entry.amount_paid or 0

            # Current value calculation using exchange rate
            current_price = entry.current_value() or 0

            labels.append(date(entry.date, "SHORT_DATE_FORMAT"))
            # We use floats here because it's easier to transpose to Django's template
            entry_prices.append(float(entry_price))
            current_prices.append(float(current_price))
            amounts_bought.append(float(entry.amount_received))

        return {
            "labels": labels,
            "entry_prices": entry_prices,
            "current_prices": current_prices,
            "amounts_bought": amounts_bought,
        }

    def current_price(self):
        exchange_rate = get_exchange_rate(
            from_currency=self.target_currency,
            to_currency=self.payment_currency,
            date=timezone.localtime(timezone.now()),
        )

        if exchange_rate:
            return exchange_rate.effective_rate, exchange_rate.date
        else:
            return None


class DCAEntry(models.Model):
    strategy = models.ForeignKey(
        DCAStrategy,
        on_delete=models.CASCADE,
        related_name="entries",
        verbose_name=_("Strategy"),
    )
    date = models.DateField(verbose_name=_("Date"))
    amount_paid = models.DecimalField(
        max_digits=42, decimal_places=30, verbose_name=_("Amount Paid")
    )
    amount_received = models.DecimalField(
        max_digits=42, decimal_places=30, verbose_name=_("Amount Received")
    )
    expense_transaction = models.ForeignKey(
        "transactions.Transaction",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="dca_expense_entries",
        verbose_name=_("Expense Transaction"),
    )
    income_transaction = models.ForeignKey(
        "transactions.Transaction",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="dca_income_entries",
        verbose_name=_("Income Transaction"),
    )
    notes = models.TextField(blank=True, null=True, verbose_name=_("Notes"))
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = _("DCA Entry")
        verbose_name_plural = _("DCA Entries")
        ordering = ["-date"]

    def __str__(self):
        return f"{self.strategy.name} - {self.date}"

    def entry_price(self):
        if self.amount_received:
            return self.amount_paid / self.amount_received
        return 0

    def current_value(self):
        """
        Calculate current value of received amount in payment currency
        using latest exchange rate
        """
        if not self.amount_received:
            return Decimal("0")

        amount, _, _, _ = convert(
            self.amount_received,
            self.strategy.target_currency,
            self.strategy.payment_currency,
        )

        return amount or Decimal("0")

    def profit_loss(self):
        """Calculate P/L in payment currency"""
        return self.current_value() - self.amount_paid

    def profit_loss_percentage(self):
        """Calculate P/L percentage"""
        if self.amount_paid:
            return (self.profit_loss() / self.amount_paid) * Decimal("100")
        return Decimal("0")
