"""Progreso del recorrido guiado."""

from django.conf import settings
from django.db import models
from django.utils import timezone

from apps.core.models import BaseModel
from apps.onboarding.content import REQUIRED_STEP_CODES, STEP_CODES


class OnboardingProgress(BaseModel):
    """
    Una fila por cuenta, para poder retomar el recorrido donde quedó.

    `context` guarda los identificadores de lo que se fue creando —el proyecto,
    la credencial, el dominio— para que retomar reutilice esos objetos en vez de
    crear duplicados. Sin eso, alguien que abandona en el paso cinco y vuelve al
    día siguiente termina con dos dominios iguales.
    """

    account = models.OneToOneField(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='onboarding'
    )
    current_step = models.CharField(max_length=40, default=STEP_CODES[0])
    completed_steps = models.JSONField(default=list)
    dismissed_at = models.DateTimeField(null=True, blank=True)
    completed_at = models.DateTimeField(null=True, blank=True)
    context = models.JSONField(default=dict)

    class Meta:
        verbose_name_plural = 'onboarding progress'

    def __str__(self) -> str:
        return f'{self.account_id}: {self.current_step}'

    @property
    def is_finished(self) -> bool:
        return self.completed_at is not None

    @property
    def is_dismissed(self) -> bool:
        return self.dismissed_at is not None

    def mark_completed(self, step_code: str) -> None:
        """
        Marca un paso como cumplido y avanza al siguiente pendiente.

        Sólo se llama después de comprobar el requisito de verdad. Marcar por
        haber visitado la pantalla haría que el recorrido termine con una
        configuración que no funciona, y el usuario se enteraría recién cuando
        no llegara ningún dato (FR-017).
        """
        if step_code not in self.completed_steps:
            self.completed_steps = [*self.completed_steps, step_code]

        # Lo que decide el final son los **obligatorios**. Un paso opcional se
        # marca cumplido como cualquier otro y aparece en la lista, pero que
        # falte no puede dejar el recorrido abierto para siempre.
        pending = [c for c in REQUIRED_STEP_CODES if c not in self.completed_steps]
        if pending:
            self.current_step = pending[0]
        else:
            self.current_step = STEP_CODES[-1]
            self.completed_at = self.completed_at or timezone.now()
