-- Template migration for each organization schema.
-- Replace {schema} with the real schema name, e.g. company_<organization_id>.

create table if not exists {schema}.customers (
    id uuid primary key,
    customer_number text unique,
    name_ciphertext bytea not null,
    name_nonce bytea not null,
    name_key_id text not null,
    status text not null default 'active',
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint customers_status_valid check (status in ('active', 'inactive', 'blocked'))
);

create table if not exists {schema}.suppliers (
    id uuid primary key,
    supplier_number text unique,
    name_ciphertext bytea not null,
    name_nonce bytea not null,
    name_key_id text not null,
    status text not null default 'active',
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint suppliers_status_valid check (status in ('active', 'inactive', 'blocked'))
);

create table if not exists {schema}.items (
    id uuid primary key,
    item_number text not null unique,
    name_ciphertext bytea not null,
    name_nonce bytea not null,
    name_key_id text not null,
    unit text not null default 'Stk',
    tax_rate numeric(7, 4) not null default 19.0,
    default_purchase_price numeric(14, 4),
    default_sales_price numeric(14, 4),
    status text not null default 'active',
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint items_status_valid check (status in ('active', 'inactive', 'blocked')),
    constraint items_tax_rate_non_negative check (tax_rate >= 0),
    constraint items_default_purchase_price_non_negative check (
        default_purchase_price is null or default_purchase_price >= 0
    ),
    constraint items_default_sales_price_non_negative check (
        default_sales_price is null or default_sales_price >= 0
    )
);

create table if not exists {schema}.cash_discount_terms (
    id uuid primary key,
    code text not null unique,
    name text not null,
    discount_percent numeric(7, 4) not null,
    discount_days integer not null,
    net_days integer,
    valid_from date,
    valid_until date,
    is_default_customer_term boolean not null default false,
    is_default_supplier_term boolean not null default false,
    is_active boolean not null default true,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint cash_discount_terms_percent_valid check (
        discount_percent >= 0 and discount_percent <= 100
    ),
    constraint cash_discount_terms_days_valid check (
        discount_days >= 0 and (net_days is null or net_days >= discount_days)
    ),
    constraint cash_discount_terms_valid_range check (
        valid_until is null or valid_from is null or valid_until >= valid_from
    )
);

create unique index if not exists idx_cash_discount_terms_default_customer
    on {schema}.cash_discount_terms (is_default_customer_term)
    where is_default_customer_term;

create unique index if not exists idx_cash_discount_terms_default_supplier
    on {schema}.cash_discount_terms (is_default_supplier_term)
    where is_default_supplier_term;

create table if not exists {schema}.customer_price_terms (
    id uuid primary key,
    customer_id uuid not null references {schema}.customers(id) on delete cascade,
    standard_discount_percent numeric(7, 4) not null default 0,
    cash_discount_term_id uuid references {schema}.cash_discount_terms(id),
    valid_from date,
    valid_until date,
    is_active boolean not null default true,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint customer_price_terms_discount_valid check (
        standard_discount_percent >= 0 and standard_discount_percent <= 100
    ),
    constraint customer_price_terms_valid_range check (
        valid_until is null or valid_from is null or valid_until >= valid_from
    )
);

create index if not exists idx_customer_price_terms_customer_active
    on {schema}.customer_price_terms (customer_id, is_active, valid_from, valid_until);

create table if not exists {schema}.supplier_price_terms (
    id uuid primary key,
    supplier_id uuid not null references {schema}.suppliers(id) on delete cascade,
    standard_discount_percent numeric(7, 4) not null default 0,
    cash_discount_term_id uuid references {schema}.cash_discount_terms(id),
    payment_days integer,
    valid_from date,
    valid_until date,
    is_active boolean not null default true,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint supplier_price_terms_discount_valid check (
        standard_discount_percent >= 0 and standard_discount_percent <= 100
    ),
    constraint supplier_price_terms_payment_days_valid check (
        payment_days is null or payment_days >= 0
    ),
    constraint supplier_price_terms_valid_range check (
        valid_until is null or valid_from is null or valid_until >= valid_from
    )
);

create index if not exists idx_supplier_price_terms_supplier_active
    on {schema}.supplier_price_terms (supplier_id, is_active, valid_from, valid_until);

create table if not exists {schema}.activities (
    id uuid primary key,
    activity_type text not null,
    title_ciphertext bytea not null,
    title_nonce bytea not null,
    title_key_id text not null,
    body_ciphertext bytea,
    body_nonce bytea,
    body_key_id text,
    status text not null default 'open',
    priority text not null default 'normal',
    due_at timestamptz,
    starts_at timestamptz,
    ends_at timestamptz,
    assigned_to_user_id uuid,
    created_by_user_id uuid,
    completed_by_user_id uuid,
    completed_at timestamptz,
    visibility text not null default 'internal',
    system_source text,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint activities_type_valid check (
        activity_type in (
            'email_note',
            'phone_note',
            'internal_note',
            'task',
            'follow_up',
            'calendar_event',
            'system_event',
            'work_step'
        )
    ),
    constraint activities_status_valid check (
        status in ('open', 'in_progress', 'done', 'cancelled')
    ),
    constraint activities_priority_valid check (
        priority in ('low', 'normal', 'high', 'critical')
    ),
    constraint activities_visibility_valid check (
        visibility in ('internal', 'organization')
    ),
    constraint activities_body_encryption_complete check (
        (
            body_ciphertext is null
            and body_nonce is null
            and body_key_id is null
        )
        or (
            body_ciphertext is not null
            and body_nonce is not null
            and body_key_id is not null
        )
    ),
    constraint activities_time_range_valid check (
        ends_at is null or starts_at is null or ends_at >= starts_at
    )
);

create index if not exists idx_activities_status_due
    on {schema}.activities (status, due_at);

create index if not exists idx_activities_assigned_status
    on {schema}.activities (assigned_to_user_id, status);

create table if not exists {schema}.activity_links (
    activity_id uuid not null references {schema}.activities(id) on delete cascade,
    entity_type text not null,
    entity_id uuid not null,
    created_at timestamptz not null default now(),
    primary key (activity_id, entity_type, entity_id),
    constraint activity_links_entity_type_valid check (
        entity_type in (
            'customer',
            'supplier',
            'contact',
            'quote',
            'outgoing_invoice',
            'incoming_invoice',
            'item',
            'document',
            'import'
        )
    )
);

create index if not exists idx_activity_links_entity
    on {schema}.activity_links (entity_type, entity_id);

create table if not exists {schema}.outgoing_invoices (
    id uuid primary key,
    invoice_number text unique,
    customer_id uuid not null references {schema}.customers(id),
    status text not null default 'draft',
    cash_discount_term_id uuid references {schema}.cash_discount_terms(id),
    issued_at date,
    due_at date,
    created_by_user_id uuid,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    finalized_at timestamptz,
    constraint outgoing_invoices_status_valid check (
        status in ('draft', 'finalized', 'sent', 'paid', 'cancelled', 'overdue')
    )
);

create index if not exists idx_outgoing_invoices_customer_status
    on {schema}.outgoing_invoices (customer_id, status);

create table if not exists {schema}.outgoing_invoice_items (
    id uuid primary key,
    invoice_id uuid not null references {schema}.outgoing_invoices(id) on delete cascade,
    line_number integer not null,
    item_id uuid not null references {schema}.items(id),
    description_ciphertext bytea,
    description_nonce bytea,
    description_key_id text,
    quantity numeric(14, 4) not null,
    unit_price numeric(14, 4) not null,
    original_unit_price numeric(14, 4),
    discount_percent numeric(7, 4) not null default 0,
    price_overridden boolean not null default false,
    price_override_reason_ciphertext bytea,
    price_override_reason_nonce bytea,
    price_override_reason_key_id text,
    price_overridden_by_user_id uuid,
    price_overridden_at timestamptz,
    tax_rate numeric(7, 4) not null,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    unique (invoice_id, line_number),
    constraint outgoing_invoice_items_quantity_positive check (quantity > 0),
    constraint outgoing_invoice_items_unit_price_non_negative check (unit_price >= 0),
    constraint outgoing_invoice_items_original_price_non_negative check (
        original_unit_price is null or original_unit_price >= 0
    ),
    constraint outgoing_invoice_items_discount_valid check (
        discount_percent >= 0 and discount_percent <= 100
    ),
    constraint outgoing_invoice_items_tax_rate_non_negative check (tax_rate >= 0),
    constraint outgoing_invoice_items_description_encryption_complete check (
        (
            description_ciphertext is null
            and description_nonce is null
            and description_key_id is null
        )
        or (
            description_ciphertext is not null
            and description_nonce is not null
            and description_key_id is not null
        )
    ),
    constraint outgoing_invoice_items_override_reason_encryption_complete check (
        (
            price_override_reason_ciphertext is null
            and price_override_reason_nonce is null
            and price_override_reason_key_id is null
        )
        or (
            price_override_reason_ciphertext is not null
            and price_override_reason_nonce is not null
            and price_override_reason_key_id is not null
        )
    ),
    constraint outgoing_invoice_items_override_complete check (
        (
            price_overridden = false
            and price_overridden_by_user_id is null
            and price_overridden_at is null
        )
        or (
            price_overridden = true
            and price_overridden_by_user_id is not null
            and price_overridden_at is not null
        )
    )
);

create index if not exists idx_outgoing_invoice_items_item
    on {schema}.outgoing_invoice_items (item_id);

create table if not exists {schema}.incoming_invoices (
    id uuid primary key,
    invoice_number text,
    supplier_id uuid not null references {schema}.suppliers(id),
    status text not null default 'draft',
    cash_discount_term_id uuid references {schema}.cash_discount_terms(id),
    invoice_date date,
    due_at date,
    created_by_user_id uuid,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    constraint incoming_invoices_status_valid check (
        status in ('draft', 'received', 'approved', 'paid', 'cancelled', 'overdue')
    )
);

create index if not exists idx_incoming_invoices_supplier_status
    on {schema}.incoming_invoices (supplier_id, status);

create table if not exists {schema}.incoming_invoice_items (
    id uuid primary key,
    invoice_id uuid not null references {schema}.incoming_invoices(id) on delete cascade,
    line_number integer not null,
    item_id uuid references {schema}.items(id),
    description_ciphertext bytea,
    description_nonce bytea,
    description_key_id text,
    quantity numeric(14, 4) not null,
    unit_price numeric(14, 4) not null,
    tax_rate numeric(7, 4) not null,
    created_at timestamptz not null default now(),
    updated_at timestamptz not null default now(),
    unique (invoice_id, line_number),
    constraint incoming_invoice_items_quantity_positive check (quantity > 0),
    constraint incoming_invoice_items_unit_price_non_negative check (unit_price >= 0),
    constraint incoming_invoice_items_tax_rate_non_negative check (tax_rate >= 0),
    constraint incoming_invoice_items_description_encryption_complete check (
        (
            description_ciphertext is null
            and description_nonce is null
            and description_key_id is null
        )
        or (
            description_ciphertext is not null
            and description_nonce is not null
            and description_key_id is not null
        )
    )
);
