"""DictIA pricing plans — v7.0 (B-2.7 refonte 2026-04-27).

Centralized plan registry. Stripe Price IDs are resolved from environment
variables — set STRIPE_<PLAN>_<PERIOD> env vars in production. The slug
(`cloud-basic`, `cloud-essentiel`, `cloud-pro`, `dictia-local`) is the
canonical identifier used throughout the codebase (URL params, webhook
metadata, audit logs).

v7.0 pricing reference (CAD, pre-tax — TPS/TVQ added by Stripe automatic_tax):
- Cloud BASIC      : 189 $/mo recurring (no setup)
                     yearly = 189 × 12 × 0.85 ≈ 1 928 $/an
- Cloud ESSENTIEL  : 349 $/mo recurring (no setup)
                     yearly = 349 × 12 × 0.85 ≈ 3 559 $/an
- Cloud PRO        : 549 $/mo recurring + 485 $ one-time onboarding setup
                     yearly = 549 × 12 × 0.85 ≈ 5 600 $/an (+ 485 $ setup)
- DictIA LOCAL     : 5 998 $ one-time (An 1 = matériel + 1ʳᵉ année logiciel)
                     puis 500 $/an dès An 2 (renewal yearly only — no monthly)

Pro+ is a sentinel plan — no Stripe Price IDs, the route redirects to
/contact?pro-plus=1 instead of opening Stripe Checkout. It exists in PLANS
so other code (URL routing, navigation) can identify it; `is_configured()`
always returns False so the route falls through to the contact redirect.
"""
import os
from dataclasses import dataclass
from typing import Dict, List, Optional


@dataclass(frozen=True)
class Plan:
    """A DictIA subscription plan (v7.0).

    Stripe Price IDs are resolved lazily from environment variables — the
    Plan instance itself only stores the variable names. This lets the
    application boot without Stripe credentials (CI, dev branches) and
    keeps secrets out of source control.

    Three pricing shapes are supported:
      - Cloud Basic / Essentiel : monthly + yearly (no setup, no renewal)
      - Cloud Pro               : monthly + yearly + setup (one-time onboarding)
      - DictIA Local            : setup (An 1) + yearly_renewal (dès An 2)
                                  — no monthly Price ID

    The Pro+ plan has all *_env fields set to None — the route checks
    `is_quote_only` and redirects to /contact instead of opening Checkout.
    """
    slug: str
    name: str
    description_fr: str
    has_setup_fee: bool
    monthly_env: Optional[str] = None
    yearly_env: Optional[str] = None
    setup_env: Optional[str] = None  # Cloud Pro setup OR DictIA Local An 1
    yearly_renewal_env: Optional[str] = None  # DictIA Local An 2+ renewal
    is_quote_only: bool = False  # True for Pro+ (no Stripe — redirect to contact)

    def setup_price_id(self) -> Optional[str]:
        if not self.has_setup_fee or not self.setup_env:
            return None
        return os.environ.get(self.setup_env)

    def monthly_price_id(self) -> Optional[str]:
        if not self.monthly_env:
            return None
        return os.environ.get(self.monthly_env)

    def yearly_price_id(self) -> Optional[str]:
        if not self.yearly_env:
            return None
        return os.environ.get(self.yearly_env)

    def yearly_renewal_price_id(self) -> Optional[str]:
        if not self.yearly_renewal_env:
            return None
        return os.environ.get(self.yearly_renewal_env)

    def is_configured(self) -> bool:
        """True when all required Stripe Price IDs are set in the environment.

        - Quote-only plans (Pro+) are never configured (always redirect to /contact).
        - Cloud plans require monthly + yearly Price IDs.
        - Cloud Pro additionally requires the one-time setup Price ID.
        - DictIA Local requires setup (An 1) + yearly_renewal (dès An 2).
        """
        if self.is_quote_only:
            return False
        # DictIA Local (one-shot + yearly renewal — no monthly)
        if self.setup_env and self.yearly_renewal_env and not self.monthly_env:
            return bool(self.setup_price_id() and self.yearly_renewal_price_id())
        # Cloud plans (monthly + yearly required, + setup if Pro)
        if self.has_setup_fee and not self.setup_price_id():
            return False
        return bool(self.monthly_price_id() and self.yearly_price_id())

    def price_id_for_period(self, period: str) -> Optional[str]:
        """Resolve the Price ID for the given billing period.

        For DictIA Local (no monthly), 'monthly' falls back to the yearly_renewal
        Price ID — Stripe Checkout will display the recurrence in the session UI.
        """
        if not self.monthly_env and self.yearly_renewal_env:
            # DictIA Local — only a yearly renewal exists
            return self.yearly_renewal_price_id()
        return self.yearly_price_id() if period == 'yearly' else self.monthly_price_id()


PLANS: Dict[str, Plan] = {
    'cloud-basic': Plan(
        slug='cloud-basic',
        name='Cloud BASIC',
        description_fr='Cloud souverain QC — 189 $/mo · solopreneur, petite équipe.',
        has_setup_fee=False,
        monthly_env='STRIPE_CLOUD_BASIC_MONTHLY',
        yearly_env='STRIPE_CLOUD_BASIC_YEARLY',
    ),
    'cloud-essentiel': Plan(
        slug='cloud-essentiel',
        name='Cloud ESSENTIEL',
        description_fr='Cloud souverain QC — 349 $/mo · cabinet en croissance.',
        has_setup_fee=False,
        monthly_env='STRIPE_CLOUD_ESSENTIEL_MONTHLY',
        yearly_env='STRIPE_CLOUD_ESSENTIEL_YEARLY',
    ),
    'cloud-pro': Plan(
        slug='cloud-pro',
        name='Cloud PRO',
        description_fr='Cloud souverain QC — 549 $/mo + 485 $ onboarding · usage intensif multi-postes.',
        has_setup_fee=True,
        setup_env='STRIPE_CLOUD_PRO_SETUP',
        monthly_env='STRIPE_CLOUD_PRO_MONTHLY',
        yearly_env='STRIPE_CLOUD_PRO_YEARLY',
    ),
    'dictia-local': Plan(
        slug='dictia-local',
        name='DictIA LOCAL',
        description_fr='100 % hors-ligne — 5 998 $ An 1 (matériel + logiciel) puis 500 $/an dès An 2.',
        has_setup_fee=True,
        setup_env='STRIPE_DICTIA_LOCAL_SETUP',
        yearly_renewal_env='STRIPE_DICTIA_LOCAL_RENEWAL_YEARLY',
        # No monthly_env / yearly_env — local plan is one-shot + yearly renewal
    ),
    'pro-plus': Plan(
        slug='pro-plus',
        name='Pro+',
        description_fr='Soumission personnalisée — > 660 h audio/mois, multi-sites, SLA 99,9 %, SOC 2.',
        has_setup_fee=False,
        is_quote_only=True,
    ),
}

VALID_PERIODS = ('monthly', 'yearly')


def get_plan(slug: str) -> Optional[Plan]:
    """Return the Plan for `slug`, or None if unknown."""
    if not slug:
        return None
    return PLANS.get(slug)


def list_plans() -> List[Plan]:
    """Return all registered plans in registration order."""
    return list(PLANS.values())
