"""
THE WORKSPACE — a user's Spaces and the Pages inside them.

This is the tree the sidebar draws, and it is the phase everything after it
depends on: every feature endpoint is scoped by `?page=<pageId>`, so a page id
has to be a server-side object before a single row can be stored.

The client's own model is `front/src/renderer/src/app/providers/SpacesProvider.jsx`,
which keeps the same tree in `localStorage` under `lifey:spaces`. Where this
file and that one disagree, that one is right — it ships today.

Four decisions carried from `front/docs/backend-plan.md` §4:

- **`Page.settings` is ONE opaque JSON blob** the server never interprets. Not a
  table per feature and not a column per key. `app/pageCopy.js` copies the RAW
  stored value on purpose, because round-tripping a settings blob through a hook
  that merges defaults turns "never configured" into a frozen snapshot of
  today's defaults.
- **`banner` holds a gradient reference, never image bytes.** A custom banner is
  a `data:` URL in `localStorage` today. Uploading images is a separate decision
  with real storage cost on a 20 GB box; until it is taken, gradients sync and
  uploads stay local.
- **`position` is a plain integer, rewritten on reorder.** A fractional index is
  the right answer at scale and over-engineering for a sidebar of a dozen rows.
  Reordering rewrites the affected rows in one transaction.
- **The page id is the SERVER'S.** The client's `uid()` ids become server ids at
  migration time.
"""

from django.conf import settings as django_settings
from django.db import models

from core.models import TimeStampedModel

# The nine page types, from `front/src/renderer/src/app/navigation.js`.
# Kept as a plain tuple rather than a `choices=` constraint on purpose: the
# client owns this list, a type added there must not need a migration here, and
# an unknown type renders `PagePlaceholder` rather than breaking a page. The
# serializer validates against it so a typo is still caught at the edge.
PAGE_TYPE_IDS = (
    'tasks',
    'habits',
    'goals',
    'calendar',
    'notes',
    'budget',
    'bookshelf',
    'focus',
    'dashboard',
)


class Space(TimeStampedModel):
    """A user-created workspace holding pages — "Private", "Work"."""

    user = models.ForeignKey(
        django_settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name='spaces',
    )
    name = models.CharField(max_length=120)
    favorite = models.BooleanField(default=False)

    # The space the app opens into, and the one the sidebar's Main view lists
    # directly. Exactly one per user carries it; `SpaceViewSet` is what keeps
    # that true, since a partial unique constraint is not portable to MySQL.
    is_default = models.BooleanField(default=False)

    position = models.IntegerField(default=0)

    class Meta:
        ordering = ['position', 'id']
        indexes = [models.Index(fields=['user', 'position'])]

    def __str__(self):
        return f'{self.name} ({self.user_id})'


class Page(TimeStampedModel):
    """One instance of a page type, inside a space."""

    space = models.ForeignKey(Space, on_delete=models.CASCADE, related_name='pages')
    type_id = models.CharField(max_length=32)
    name = models.CharField(max_length=120)

    # `{id, color}` — the icon picker's shape. Null means "use the type's own
    # icon", which is not the same as a stored icon that happens to match it.
    icon = models.JSONField(null=True, blank=True)

    # `{type: 'gradient', id}`. An image banner is `{type: 'image', posY}` and
    # its BYTES stay on the machine that picked them — see the module docstring.
    banner = models.JSONField(null=True, blank=True)

    # Pins the page to the sidebar's Favourites section. Distinct from a
    # Space's `favorite`, which pins the space.
    favorite = models.BooleanField(default=False)

    # Blocks edits to the page's CONTENT while rename and re-icon still work.
    # Not the same gate as focus mode, which hides presentation controls.
    locked = models.BooleanField(default=False)

    position = models.IntegerField(default=0)
    settings = models.JSONField(default=dict, blank=True)

    class Meta:
        ordering = ['position', 'id']
        indexes = [models.Index(fields=['space', 'position'])]

    def __str__(self):
        return f'{self.name} [{self.type_id}]'
