from datetime import datetime
from typing import Optional
from uuid import UUID, uuid1, uuid4

import sqlalchemy as sa
import sqlalchemy.dialects.postgresql as pg
from sqlmodel import Field, SQLModel

from .payload_column_type import PayloadContainer, payload_column_type


def generate_time_uuid(node=None, clock_seq=None):
    """Create a lexicographically sortable time ordered custom (non-standard) UUID by reordering the timestamp fields of a version 1 UUID."""
    (time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node) = uuid1(node, clock_seq).fields
    # reconstruct 60 bit timestamp, see version 1 uuid: https://www.rfc-editor.org/rfc/rfc4122
    timestamp = (time_hi_version & 0xFFF) << 48 | (time_mid << 32) | time_low
    version = time_hi_version >> 12
    assert version == 1
    a = timestamp >> 28  # bits 28-59
    b = (timestamp >> 12) & 0xFFFF  # bits 12-27
    c = timestamp & 0xFFF  # bits 0-11 (clear version bits)
    clock_seq_hi_variant &= 0xF  # (clear variant bits)
    return UUID(fields=(a, b, c, clock_seq_hi_variant, clock_seq_low, node), version=None)


class Journal(SQLModel, table=True):
    __tablename__ = "journal"

    id: Optional[UUID] = Field(
        sa_column=sa.Column(pg.UUID(as_uuid=True), primary_key=True, default=generate_time_uuid),
    )
    created_date: Optional[datetime] = Field(
        sa_column=sa.Column(sa.DateTime(timezone=True), nullable=False, server_default=sa.func.current_timestamp())
    )
    user_id: Optional[UUID] = Field(nullable=True, foreign_key="user.id", index=True)
    message_id: Optional[UUID] = Field(foreign_key="message.id", nullable=True)
    api_client_id: UUID = Field(foreign_key="api_client.id")

    event_type: str = Field(nullable=False, max_length=200)
    event_payload: PayloadContainer = Field(sa_column=sa.Column(payload_column_type(PayloadContainer), nullable=False))


class JournalIntegration(SQLModel, table=True):
    __tablename__ = "journal_integration"

    id: Optional[UUID] = Field(
        sa_column=sa.Column(
            pg.UUID(as_uuid=True), primary_key=True, default=uuid4, server_default=sa.text("gen_random_uuid()")
        ),
    )
    description: str = Field(max_length=512, primary_key=True)
    last_journal_id: Optional[UUID] = Field(foreign_key="journal.id", nullable=True)
    last_run: Optional[datetime] = Field(sa_column=sa.Column(sa.DateTime(), nullable=True))
    last_error: Optional[str] = Field(nullable=True)
    next_run: Optional[datetime] = Field(nullable=True)