from decimal import Decimal

from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models


class Portfolio(models.Model):
    """Represents an investment account/portfolio (e.g., 'MOMO', 'User IBKR')."""

    name = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

    class Meta:
        ordering = ['name']


class Stock(models.Model):
    """Current holdings for a portfolio. Quantity only — prices are fetched on demand."""

    portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='stocks')
    stock_code = models.CharField(max_length=20, help_text="Stock ticker, e.g. 'NVDA', '9988.HK'")
    quantity = models.DecimalField(
        max_digits=20,
        decimal_places=6,
        default=Decimal('0'),
        validators=[MinValueValidator(Decimal('0'))],
    )

    class Meta:
        unique_together = [('portfolio', 'stock_code')]
        ordering = ['stock_code']

    def __str__(self):
        return f"{self.stock_code} ({self.portfolio.name})"


class Transaction(models.Model):
    """
    Buy/sell event log.

    Price/currency/fee are intentionally optional: broker screenshots and AI/OCR syncs often
    only provide ticker + quantity. When present, these fields enable cost basis and P&L.
    """

    ACTION_BUY = 'BUY'
    ACTION_SELL = 'SELL'
    ACTION_CHOICES = [(ACTION_BUY, 'Buy'), (ACTION_SELL, 'Sell')]

    portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='transactions')
    action = models.CharField(max_length=4, choices=ACTION_CHOICES)
    stock_code = models.CharField(max_length=20)
    quantity = models.DecimalField(
        max_digits=20,
        decimal_places=6,
        validators=[MinValueValidator(Decimal('0.000001'))],
    )
    price_per_share = models.DecimalField(
        max_digits=20,
        decimal_places=6,
        null=True,
        blank=True,
        validators=[MinValueValidator(Decimal('0'))],
        help_text='Optional execution price per share.',
    )
    currency = models.CharField(max_length=3, default='USD')
    fee = models.DecimalField(
        max_digits=20,
        decimal_places=6,
        null=True,
        blank=True,
        validators=[MinValueValidator(Decimal('0'))],
        help_text='Optional broker fee/commission in transaction currency.',
    )
    broker_trade_id = models.CharField(max_length=128, blank=True, default='')
    source = models.CharField(max_length=50, blank=True, default='')
    confidence = models.DecimalField(
        max_digits=5,
        decimal_places=4,
        null=True,
        blank=True,
        validators=[MinValueValidator(Decimal('0')), MaxValueValidator(Decimal('1'))],
    )
    date = models.DateField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-date', '-created_at']
        indexes = [
            models.Index(fields=['portfolio', 'date']),
            models.Index(fields=['stock_code', 'date']),
        ]

    def __str__(self):
        return f"{self.action} {self.quantity} {self.stock_code} on {self.date}"


class CashFlow(models.Model):
    """External/internal cash ledger used for cash-flow-adjusted performance."""

    FLOW_DEPOSIT = 'DEPOSIT'
    FLOW_WITHDRAWAL = 'WITHDRAWAL'
    FLOW_DIVIDEND = 'DIVIDEND'
    FLOW_FEE = 'FEE'
    FLOW_INTEREST = 'INTEREST'
    FLOW_TRANSFER_IN = 'TRANSFER_IN'
    FLOW_TRANSFER_OUT = 'TRANSFER_OUT'

    FLOW_CHOICES = [
        (FLOW_DEPOSIT, 'Deposit'),
        (FLOW_WITHDRAWAL, 'Withdrawal'),
        (FLOW_DIVIDEND, 'Dividend'),
        (FLOW_FEE, 'Fee'),
        (FLOW_INTEREST, 'Interest'),
        (FLOW_TRANSFER_IN, 'Transfer In'),
        (FLOW_TRANSFER_OUT, 'Transfer Out'),
    ]

    EXTERNAL_POSITIVE = {FLOW_DEPOSIT, FLOW_TRANSFER_IN}
    EXTERNAL_NEGATIVE = {FLOW_WITHDRAWAL, FLOW_TRANSFER_OUT}
    VALUE_POSITIVE = {FLOW_DEPOSIT, FLOW_TRANSFER_IN, FLOW_DIVIDEND, FLOW_INTEREST}
    VALUE_NEGATIVE = {FLOW_WITHDRAWAL, FLOW_TRANSFER_OUT, FLOW_FEE}

    portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='cashflows')
    flow_type = models.CharField(max_length=20, choices=FLOW_CHOICES)
    amount = models.DecimalField(
        max_digits=20,
        decimal_places=2,
        validators=[MinValueValidator(Decimal('0.01'))],
    )
    currency = models.CharField(max_length=3, default='USD')
    date = models.DateField()
    source = models.CharField(max_length=50, blank=True, default='')
    note = models.TextField(blank=True, default='')
    confidence = models.DecimalField(
        max_digits=5,
        decimal_places=4,
        null=True,
        blank=True,
        validators=[MinValueValidator(Decimal('0')), MaxValueValidator(Decimal('1'))],
    )
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-date', '-created_at']
        indexes = [
            models.Index(fields=['portfolio', 'date']),
            models.Index(fields=['flow_type', 'date']),
        ]

    @property
    def signed_amount(self) -> Decimal:
        if self.flow_type in self.VALUE_NEGATIVE:
            return -self.amount
        return self.amount

    @property
    def external_signed_amount(self) -> Decimal:
        if self.flow_type in self.EXTERNAL_POSITIVE:
            return self.amount
        if self.flow_type in self.EXTERNAL_NEGATIVE:
            return -self.amount
        return Decimal('0')

    @property
    def is_external(self) -> bool:
        return self.flow_type in self.EXTERNAL_POSITIVE.union(self.EXTERNAL_NEGATIVE)

    def __str__(self):
        return f"{self.flow_type} {self.amount} {self.currency} ({self.portfolio.name}) on {self.date}"


class PortfolioSnapshot(models.Model):
    """Periodic total-value snapshot per portfolio."""

    portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='snapshots')
    captured_at = models.DateTimeField()
    total_value = models.DecimalField(max_digits=20, decimal_places=2)

    class Meta:
        ordering = ['-captured_at']
        indexes = [
            models.Index(fields=['portfolio', 'captured_at']),
        ]

    def __str__(self):
        return f"{self.portfolio.name} @ {self.captured_at:%Y-%m-%d %H:%M}: ${self.total_value}"


class BenchmarkPrice(models.Model):
    """Daily close for benchmark tickers (QQQ, SPY, etc.) cached from market data."""

    ticker = models.CharField(max_length=20)
    date = models.DateField()
    close = models.DecimalField(max_digits=20, decimal_places=6)

    class Meta:
        unique_together = [('ticker', 'date')]
        indexes = [
            models.Index(fields=['ticker', 'date']),
        ]
        ordering = ['ticker', 'date']

    def __str__(self):
        return f"{self.ticker} {self.date}: ${self.close}"
