BEGIN;

SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;

ALTER TABLE IF EXISTS clubs
  ADD COLUMN IF NOT EXISTS short_name varchar(120),
  ADD COLUMN IF NOT EXISTS legal_name varchar(255),
  ADD COLUMN IF NOT EXISTS club_number varchar(64),
  ADD COLUMN IF NOT EXISTS email varchar(255),
  ADD COLUMN IF NOT EXISTS phone varchar(80),
  ADD COLUMN IF NOT EXISTS website varchar(255),
  ADD COLUMN IF NOT EXISTS street varchar(255),
  ADD COLUMN IF NOT EXISTS postal_code varchar(24),
  ADD COLUMN IF NOT EXISTS city varchar(120),
  ADD COLUMN IF NOT EXISTS country_code varchar(2) DEFAULT 'DE',
  ADD COLUMN IF NOT EXISTS chairperson_name varchar(255),
  ADD COLUMN IF NOT EXISTS treasurer_name varchar(255),
  ADD COLUMN IF NOT EXISTS youth_manager_name varchar(255),
  ADD COLUMN IF NOT EXISTS creditor_identifier varchar(64),
  ADD COLUMN IF NOT EXISTS billing_email varchar(255),
  ADD COLUMN IF NOT EXISTS iban varchar(34),
  ADD COLUMN IF NOT EXISTS bic varchar(11),
  ADD COLUMN IF NOT EXISTS fee_rules jsonb,
  ADD COLUMN IF NOT EXISTS outgoing_invoice_prefix varchar(24) NOT NULL DEFAULT 'RE',
  ADD COLUMN IF NOT EXISTS outgoing_invoice_next_number integer NOT NULL DEFAULT 1,
  ADD COLUMN IF NOT EXISTS incoming_invoice_prefix varchar(24) NOT NULL DEFAULT 'EI',
  ADD COLUMN IF NOT EXISTS incoming_invoice_next_number integer NOT NULL DEFAULT 1,
  ADD COLUMN IF NOT EXISTS is_archived boolean NOT NULL DEFAULT false,
  ADD COLUMN IF NOT EXISTS archived_at timestamptz;

ALTER TABLE IF EXISTS member
  ADD COLUMN IF NOT EXISTS member_number varchar(64),
  ADD COLUMN IF NOT EXISTS membership_status varchar(32) NOT NULL DEFAULT 'active',
  ADD COLUMN IF NOT EXISTS membership_type varchar(32) NOT NULL DEFAULT 'regular',
  ADD COLUMN IF NOT EXISTS joined_on date,
  ADD COLUMN IF NOT EXISTS left_on date,
  ADD COLUMN IF NOT EXISTS contribution_group_code varchar(64),
  ADD COLUMN IF NOT EXISTS needs_sepa_mandate boolean NOT NULL DEFAULT false,
  ADD COLUMN IF NOT EXISTS sepa_mandate_reference varchar(80),
  ADD COLUMN IF NOT EXISTS is_archived boolean NOT NULL DEFAULT false,
  ADD COLUMN IF NOT EXISTS archived_at timestamptz,
  ADD COLUMN IF NOT EXISTS archived_reason text;

CREATE TABLE IF NOT EXISTS club_requests (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  request_type varchar(32) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'open',
  workflow_stage varchar(64),
  priority varchar(16) NOT NULL DEFAULT 'normal',
  source_system varchar(64),
  source_reference varchar(255),
  subject varchar(255),
  first_name varchar(120),
  last_name varchar(120),
  email varchar(255),
  phone varchar(80),
  birthdate date,
  message text,
  requested_membership_type varchar(32),
  assigned_user_id bigint,
  assigned_member_id bigint,
  converted_member_id bigint,
  received_at timestamptz NOT NULL DEFAULT now(),
  closed_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE TABLE IF NOT EXISTS club_distribution_groups (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  name varchar(255) NOT NULL,
  description text,
  group_type varchar(32) NOT NULL DEFAULT 'custom',
  is_system_group boolean NOT NULL DEFAULT false,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_distribution_groups_club
  ON club_distribution_groups (club_id, name);

CREATE TABLE IF NOT EXISTS club_distribution_group_members (
  id bigserial PRIMARY KEY,
  group_id bigint NOT NULL,
  member_id bigint NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE (group_id, member_id)
);

CREATE INDEX IF NOT EXISTS idx_club_distribution_group_members_member
  ON club_distribution_group_members (member_id);

CREATE TABLE IF NOT EXISTS club_communication_threads (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  thread_type varchar(32) NOT NULL DEFAULT 'direct',
  subject varchar(255) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'draft',
  created_by_user_id bigint,
  distribution_group_id bigint,
  recipient_member_id bigint,
  scheduled_at timestamptz,
  sent_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_threads_club
  ON club_communication_threads (club_id, status, thread_type);

CREATE TABLE IF NOT EXISTS club_communication_messages (
  id bigserial PRIMARY KEY,
  thread_id bigint NOT NULL,
  club_id bigint NOT NULL,
  message_type varchar(32) NOT NULL DEFAULT 'message',
  direction varchar(32) NOT NULL DEFAULT 'outbound',
  body text NOT NULL,
  created_by_user_id bigint,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_messages_thread
  ON club_communication_messages (thread_id, created_at DESC);

CREATE TABLE IF NOT EXISTS club_communication_recipients (
  id bigserial PRIMARY KEY,
  thread_id bigint NOT NULL,
  club_id bigint NOT NULL,
  member_id bigint,
  recipient_name varchar(255) NOT NULL,
  email_snapshot varchar(255),
  delivery_status varchar(32) NOT NULL DEFAULT 'pending',
  delivered_at timestamptz,
  last_attempt_at timestamptz,
  attempt_count integer NOT NULL DEFAULT 0,
  retryable boolean NOT NULL DEFAULT false,
  error_code varchar(64),
  transport_message_id varchar(255),
  error_message text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_recipients_thread
  ON club_communication_recipients (thread_id, delivery_status);

CREATE TABLE IF NOT EXISTS club_communication_delivery_logs (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  thread_id bigint NOT NULL,
  recipient_id bigint,
  created_by_user_id bigint,
  status varchar(32) NOT NULL DEFAULT 'failed',
  attempt_no integer NOT NULL DEFAULT 1,
  retryable boolean NOT NULL DEFAULT false,
  error_code varchar(64),
  error_message text,
  transport_message_id varchar(255),
  transport_response text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_delivery_logs_thread
  ON club_communication_delivery_logs (thread_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_club_communication_delivery_logs_recipient
  ON club_communication_delivery_logs (recipient_id);

CREATE INDEX IF NOT EXISTS idx_club_requests_club_status
  ON club_requests (club_id, status, request_type);

CREATE TABLE IF NOT EXISTS club_request_notes (
  id bigserial PRIMARY KEY,
  club_request_id bigint NOT NULL,
  note_type varchar(32) NOT NULL DEFAULT 'internal',
  body text NOT NULL,
  created_by_user_id bigint,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_request_notes_request
  ON club_request_notes (club_request_id, created_at DESC);

CREATE TABLE IF NOT EXISTS club_distribution_groups (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  name varchar(160) NOT NULL,
  description text,
  group_type varchar(32) NOT NULL DEFAULT 'manual',
  is_system_group boolean NOT NULL DEFAULT false,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_distribution_groups_name
  ON club_distribution_groups (club_id, lower(name));

CREATE TABLE IF NOT EXISTS club_distribution_group_members (
  id bigserial PRIMARY KEY,
  distribution_group_id bigint NOT NULL,
  member_id bigint,
  user_id bigint,
  email varchar(255),
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_distribution_group_members_group
  ON club_distribution_group_members (distribution_group_id);

CREATE TABLE IF NOT EXISTS club_communication_threads (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  thread_type varchar(32) NOT NULL,
  subject varchar(255) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'draft',
  created_by_user_id bigint,
  scheduled_at timestamptz,
  sent_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_threads_club_status
  ON club_communication_threads (club_id, status, thread_type);

CREATE TABLE IF NOT EXISTS club_communication_recipients (
  id bigserial PRIMARY KEY,
  thread_id bigint NOT NULL,
  member_id bigint,
  user_id bigint,
  distribution_group_id bigint,
  email varchar(255),
  delivery_status varchar(32) NOT NULL DEFAULT 'pending',
  delivered_at timestamptz,
  read_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_recipients_thread
  ON club_communication_recipients (thread_id, delivery_status);

CREATE TABLE IF NOT EXISTS club_communication_messages (
  id bigserial PRIMARY KEY,
  thread_id bigint NOT NULL,
  sender_user_id bigint,
  sender_member_id bigint,
  body text NOT NULL,
  body_format varchar(16) NOT NULL DEFAULT 'plain',
  sent_at timestamptz NOT NULL DEFAULT now(),
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_messages_thread
  ON club_communication_messages (thread_id, sent_at);

CREATE TABLE IF NOT EXISTS club_events (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  event_type varchar(32) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'planned',
  title varchar(255) NOT NULL,
  description text,
  location varchar(255),
  starts_at timestamptz NOT NULL,
  ends_at timestamptz,
  registration_deadline timestamptz,
  organizer_user_id bigint,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  archived_at timestamptz
);

CREATE INDEX IF NOT EXISTS idx_club_events_club_starts
  ON club_events (club_id, starts_at);

CREATE TABLE IF NOT EXISTS club_event_participants (
  id bigserial PRIMARY KEY,
  event_id bigint NOT NULL,
  member_id bigint,
  user_id bigint,
  role_code varchar(32),
  participation_status varchar(32) NOT NULL DEFAULT 'planned',
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_event_participants_event
  ON club_event_participants (event_id, participation_status);

CREATE TABLE IF NOT EXISTS club_documents (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  document_type varchar(32) NOT NULL,
  title varchar(255) NOT NULL,
  description text,
  status varchar(32) NOT NULL DEFAULT 'active',
  visibility_scope varchar(32) NOT NULL DEFAULT 'board',
  owner_user_id bigint,
  current_version_no integer NOT NULL DEFAULT 1,
  archived_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_documents_club_type
  ON club_documents (club_id, document_type, status);

CREATE TABLE IF NOT EXISTS club_document_versions (
  id bigserial PRIMARY KEY,
  document_id bigint NOT NULL,
  version_no integer NOT NULL,
  file_name varchar(255) NOT NULL,
  storage_path varchar(500) NOT NULL,
  mime_type varchar(120),
  file_size_bytes bigint,
  checksum_sha256 varchar(64),
  uploaded_by_user_id bigint,
  uploaded_at timestamptz NOT NULL DEFAULT now(),
  change_note text
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_document_versions_document_version
  ON club_document_versions (document_id, version_no);

CREATE TABLE IF NOT EXISTS club_document_links (
  id bigserial PRIMARY KEY,
  document_id bigint NOT NULL,
  linked_entity_type varchar(32) NOT NULL,
  linked_entity_id bigint NOT NULL,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_document_links_entity
  ON club_document_links (linked_entity_type, linked_entity_id);

CREATE TABLE IF NOT EXISTS club_tasks (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  title varchar(255) NOT NULL,
  task_type varchar(64),
  description text,
  status varchar(32) NOT NULL DEFAULT 'open',
  priority varchar(16) NOT NULL DEFAULT 'normal',
  due_at timestamptz,
  remind_at timestamptz,
  created_by_user_id bigint,
  assigned_user_id bigint,
  automation_source varchar(64),
  automation_key varchar(255),
  related_entity_type varchar(32),
  related_entity_id bigint,
  source_snapshot jsonb,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  completed_at timestamptz,
  archived_at timestamptz
);

CREATE INDEX IF NOT EXISTS idx_club_tasks_club_status_due
  ON club_tasks (club_id, status, due_at);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_tasks_automation_key
  ON club_tasks (club_id, automation_key)
  WHERE automation_key IS NOT NULL;

CREATE TABLE IF NOT EXISTS club_task_suppressions (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  automation_key varchar(255) NOT NULL,
  suppression_token varchar(255) NOT NULL,
  dismissed_by_user_id bigint,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_task_suppressions_key
  ON club_task_suppressions (club_id, automation_key);

CREATE INDEX IF NOT EXISTS idx_club_task_suppressions_lookup
  ON club_task_suppressions (club_id, automation_key, suppression_token);

CREATE TABLE IF NOT EXISTS club_sponsors (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  name varchar(255) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'lead',
  website varchar(255),
  email varchar(255),
  phone varchar(80),
  street varchar(255),
  postal_code varchar(24),
  city varchar(120),
  notes text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  archived_at timestamptz
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_sponsors_name
  ON club_sponsors (club_id, lower(name));

CREATE TABLE IF NOT EXISTS club_sponsor_contacts (
  id bigserial PRIMARY KEY,
  sponsor_id bigint NOT NULL,
  first_name varchar(120),
  last_name varchar(120),
  role_title varchar(120),
  email varchar(255),
  phone varchar(80),
  is_primary boolean NOT NULL DEFAULT false,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_sponsor_contacts_sponsor
  ON club_sponsor_contacts (sponsor_id, is_primary DESC);

CREATE TABLE IF NOT EXISTS club_sponsor_contracts (
  id bigserial PRIMARY KEY,
  sponsor_id bigint NOT NULL,
  title varchar(255) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'draft',
  starts_on date,
  ends_on date,
  annual_amount_cents bigint,
  currency_code varchar(3) NOT NULL DEFAULT 'EUR',
  notes text,
  document_id bigint,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_sponsor_contracts_sponsor
  ON club_sponsor_contracts (sponsor_id, status);

CREATE TABLE IF NOT EXISTS club_fee_rules (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  code varchar(64) NOT NULL,
  name varchar(255) NOT NULL,
  category varchar(32) NOT NULL DEFAULT 'membership',
  billing_cycle varchar(16) NOT NULL DEFAULT 'monthly',
  base_amount_cents bigint NOT NULL,
  currency_code varchar(3) NOT NULL DEFAULT 'EUR',
  age_from integer,
  age_to integer,
  is_family_rule boolean NOT NULL DEFAULT false,
  is_reduction_rule boolean NOT NULL DEFAULT false,
  valid_from date NOT NULL DEFAULT CURRENT_DATE,
  valid_to date,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_fee_rules_code
  ON club_fee_rules (club_id, code);

CREATE TABLE IF NOT EXISTS club_fee_rule_assignments (
  id bigserial PRIMARY KEY,
  fee_rule_id bigint NOT NULL,
  member_id bigint NOT NULL,
  valid_from date NOT NULL DEFAULT CURRENT_DATE,
  valid_to date,
  discount_percent numeric(5,2),
  fixed_amount_cents bigint,
  notes text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_fee_rule_assignments_member
  ON club_fee_rule_assignments (member_id, valid_from DESC);

CREATE TABLE IF NOT EXISTS club_payment_accounts (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  account_name varchar(255) NOT NULL,
  account_type varchar(32) NOT NULL DEFAULT 'bank',
  iban varchar(34),
  bic varchar(11),
  bank_name varchar(255),
  account_holder varchar(255),
  is_default 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()
);

CREATE INDEX IF NOT EXISTS idx_club_payment_accounts_club
  ON club_payment_accounts (club_id, is_default DESC, is_active DESC);

CREATE TABLE IF NOT EXISTS club_sepa_mandates (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  member_id bigint,
  debtor_name varchar(255) NOT NULL,
  iban varchar(34) NOT NULL,
  bic varchar(11),
  mandate_reference varchar(80) NOT NULL,
  signed_on date,
  valid_from date,
  revoked_at timestamptz,
  status varchar(32) NOT NULL DEFAULT 'active',
  history_note text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_sepa_mandates_reference
  ON club_sepa_mandates (club_id, mandate_reference);

CREATE TABLE IF NOT EXISTS club_payment_claims (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  member_id bigint,
  fee_rule_id bigint,
  claim_type varchar(32) NOT NULL DEFAULT 'membership_fee',
  status varchar(32) NOT NULL DEFAULT 'open',
  due_on date NOT NULL,
  amount_cents bigint NOT NULL,
  currency_code varchar(3) NOT NULL DEFAULT 'EUR',
  reminder_level integer NOT NULL DEFAULT 0,
  last_reminder_at timestamptz,
  notes text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  settled_at timestamptz,
  archived_at timestamptz
);

CREATE INDEX IF NOT EXISTS idx_club_payment_claims_club_status_due
  ON club_payment_claims (club_id, status, due_on);

CREATE TABLE IF NOT EXISTS club_accounts (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  name varchar(160) NOT NULL,
  account_holder varchar(255),
  bank_name varchar(160),
  iban varchar(34),
  bic varchar(11),
  account_type varchar(16) NOT NULL DEFAULT 'bank',
  usage_type varchar(32) NOT NULL DEFAULT 'general',
  currency_code varchar(3) NOT NULL DEFAULT 'EUR',
  allow_sepa_collections boolean NOT NULL DEFAULT false,
  allow_outgoing_payments boolean NOT NULL DEFAULT true,
  is_default boolean NOT NULL DEFAULT false,
  status varchar(16) NOT NULL DEFAULT 'active',
  notes text,
  sort_order integer NOT NULL DEFAULT 0,
  archived_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_accounts_club_status
  ON club_accounts (club_id, status, account_type);

CREATE INDEX IF NOT EXISTS idx_club_accounts_default
  ON club_accounts (club_id, is_default);

CREATE TABLE IF NOT EXISTS club_payment_entries (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  payment_claim_id bigint,
  payment_account_id bigint,
  entry_type varchar(32) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'booked',
  booked_on date NOT NULL DEFAULT CURRENT_DATE,
  amount_cents bigint NOT NULL,
  currency_code varchar(3) NOT NULL DEFAULT 'EUR',
  reference_text varchar(255),
  external_reference varchar(255),
  created_by_user_id bigint,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_payment_entries_club_booked
  ON club_payment_entries (club_id, booked_on DESC);

CREATE TABLE IF NOT EXISTS club_invoice_parties (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  party_type varchar(32) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'active',
  name varchar(255) NOT NULL,
  contract_reference varchar(120),
  valid_from date,
  valid_to date,
  contact_name varchar(255),
  email varchar(255),
  phone varchar(80),
  street varchar(255),
  postal_code varchar(24),
  city varchar(120),
  country_code varchar(2) DEFAULT 'DE',
  iban varchar(34),
  bic varchar(11),
  tax_identifier varchar(64),
  notes text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_invoice_parties_club_type
  ON club_invoice_parties (club_id, party_type);
CREATE INDEX IF NOT EXISTS idx_club_invoice_parties_club_status
  ON club_invoice_parties (club_id, status);
CREATE INDEX IF NOT EXISTS idx_club_invoice_parties_club_valid_from
  ON club_invoice_parties (club_id, valid_from);
CREATE INDEX IF NOT EXISTS idx_club_invoice_parties_club_valid_to
  ON club_invoice_parties (club_id, valid_to);

CREATE TABLE IF NOT EXISTS club_invoices (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  invoice_direction varchar(16) NOT NULL,
  invoice_type varchar(32) NOT NULL,
  status varchar(32) NOT NULL DEFAULT 'draft',
  invoice_number varchar(64),
  external_reference varchar(255),
  party_id bigint,
  account_id bigint,
  issued_on date,
  due_on date,
  paid_on date,
  net_amount_cents bigint NOT NULL DEFAULT 0,
  tax_amount_cents bigint NOT NULL DEFAULT 0,
  gross_amount_cents bigint NOT NULL DEFAULT 0,
  currency_code varchar(3) NOT NULL DEFAULT 'EUR',
  description text,
  document_id bigint,
  created_by_user_id bigint,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now(),
  archived_at timestamptz
);

CREATE INDEX IF NOT EXISTS idx_club_invoices_club_direction_status
  ON club_invoices (club_id, invoice_direction, status, due_on);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_invoices_number
  ON club_invoices (club_id, invoice_number)
  WHERE invoice_number IS NOT NULL;

CREATE TABLE IF NOT EXISTS club_invoice_items (
  id bigserial PRIMARY KEY,
  invoice_id bigint NOT NULL,
  line_no integer NOT NULL DEFAULT 1,
  description text NOT NULL,
  quantity numeric(12,2) NOT NULL DEFAULT 1,
  unit_price_cents bigint NOT NULL DEFAULT 0,
  tax_rate numeric(5,2) NOT NULL DEFAULT 0,
  total_cents bigint NOT NULL DEFAULT 0,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_club_invoice_items_line
  ON club_invoice_items (invoice_id, line_no);

CREATE TABLE IF NOT EXISTS club_history_entries (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  entity_type varchar(32) NOT NULL,
  entity_id bigint NOT NULL,
  action_type varchar(32) NOT NULL,
  actor_user_id bigint,
  actor_member_id bigint,
  old_value_json jsonb,
  new_value_json jsonb,
  summary text,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_history_entries_club_entity
  ON club_history_entries (club_id, entity_type, entity_id, created_at DESC);

CREATE TABLE IF NOT EXISTS club_user_roles (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  user_id bigint,
  member_id bigint,
  role_code varchar(32) NOT NULL,
  valid_from date NOT NULL DEFAULT CURRENT_DATE,
  valid_to date,
  is_primary boolean NOT NULL DEFAULT false,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_user_roles_club_user
  ON club_user_roles (club_id, user_id, role_code);

ALTER TABLE club_tasks
  ADD COLUMN IF NOT EXISTS task_type varchar(64),
  ADD COLUMN IF NOT EXISTS automation_source varchar(64),
  ADD COLUMN IF NOT EXISTS automation_key varchar(255),
  ADD COLUMN IF NOT EXISTS source_snapshot jsonb;

ALTER TABLE club_requests
  ADD COLUMN IF NOT EXISTS workflow_stage varchar(64);

DO $$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_requests_type') THEN
    ALTER TABLE club_requests
      ADD CONSTRAINT chk_club_requests_type
      CHECK (request_type IN ('contact', 'trial_training', 'membership', 'sponsoring'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_requests_status') THEN
    ALTER TABLE club_requests
      ADD CONSTRAINT chk_club_requests_status
      CHECK (status IN ('open', 'in_progress', 'waiting', 'converted', 'rejected', 'archived'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_events_type') THEN
    ALTER TABLE club_events
      ADD CONSTRAINT chk_club_events_type
      CHECK (event_type IN ('training', 'match', 'club_event', 'meeting', 'deadline'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_tasks_status') THEN
    ALTER TABLE club_tasks
      ADD CONSTRAINT chk_club_tasks_status
      CHECK (status IN ('open', 'in_progress', 'waiting', 'done', 'cancelled', 'archived'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_fee_rules_cycle') THEN
    ALTER TABLE club_fee_rules
      ADD CONSTRAINT chk_club_fee_rules_cycle
      CHECK (billing_cycle IN ('monthly', 'quarterly', 'half_yearly', 'yearly', 'one_time'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_payment_claims_status') THEN
    ALTER TABLE club_payment_claims
      ADD CONSTRAINT chk_club_payment_claims_status
      CHECK (status IN ('open', 'partially_paid', 'paid', 'written_off', 'cancelled'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_accounts_type') THEN
    ALTER TABLE club_accounts
      ADD CONSTRAINT chk_club_accounts_type
      CHECK (account_type IN ('bank', 'cash', 'virtual'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_accounts_usage') THEN
    ALTER TABLE club_accounts
      ADD CONSTRAINT chk_club_accounts_usage
      CHECK (usage_type IN ('general', 'membership_fees', 'donations', 'expenses', 'reserve', 'petty_cash'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_accounts_status') THEN
    ALTER TABLE club_accounts
      ADD CONSTRAINT chk_club_accounts_status
      CHECK (status IN ('active', 'inactive', 'archived'));
  END IF;

  IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_club_invoices_direction') THEN
    ALTER TABLE club_invoices
      ADD CONSTRAINT chk_club_invoices_direction
      CHECK (invoice_direction IN ('incoming', 'outgoing'));
  END IF;
END
$$;

ALTER TABLE IF EXISTS club_communication_recipients
  ADD COLUMN IF NOT EXISTS last_attempt_at timestamptz,
  ADD COLUMN IF NOT EXISTS attempt_count integer NOT NULL DEFAULT 0,
  ADD COLUMN IF NOT EXISTS retryable boolean NOT NULL DEFAULT false,
  ADD COLUMN IF NOT EXISTS error_code varchar(64),
  ADD COLUMN IF NOT EXISTS transport_message_id varchar(255);

CREATE TABLE IF NOT EXISTS club_communication_delivery_logs (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  thread_id bigint NOT NULL,
  recipient_id bigint,
  created_by_user_id bigint,
  status varchar(32) NOT NULL DEFAULT 'failed',
  attempt_no integer NOT NULL DEFAULT 1,
  retryable boolean NOT NULL DEFAULT false,
  error_code varchar(64),
  error_message text,
  transport_message_id varchar(255),
  transport_response text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_delivery_logs_thread
  ON club_communication_delivery_logs (thread_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_club_communication_delivery_logs_recipient
  ON club_communication_delivery_logs (recipient_id);

ALTER TABLE IF EXISTS club_distribution_groups
  ADD COLUMN IF NOT EXISTS filter_definition jsonb;

ALTER TABLE IF EXISTS club_communication_threads
  ADD COLUMN IF NOT EXISTS recipient_filters jsonb;

CREATE TABLE IF NOT EXISTS club_communication_templates (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  name varchar(160) NOT NULL,
  category varchar(64) NOT NULL DEFAULT 'general',
  subject_template varchar(255),
  body_template text,
  variables_hint text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_communication_templates_club
  ON club_communication_templates (club_id, category, name);

CREATE TABLE IF NOT EXISTS club_account_transactions (
  id bigserial PRIMARY KEY,
  club_id bigint NOT NULL,
  account_id bigint NOT NULL,
  invoice_id bigint,
  created_by_user_id bigint,
  direction varchar(16) NOT NULL DEFAULT 'credit',
  booking_type varchar(32) NOT NULL DEFAULT 'manual',
  status varchar(32) NOT NULL DEFAULT 'booked',
  booking_date date NOT NULL,
  value_date date,
  amount_cents bigint NOT NULL DEFAULT 0,
  currency_code varchar(3) NOT NULL DEFAULT 'EUR',
  reference varchar(255),
  notes text,
  created_at timestamptz NOT NULL DEFAULT now(),
  updated_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_club_account_transactions_club_date
  ON club_account_transactions (club_id, booking_date DESC, status);

CREATE INDEX IF NOT EXISTS idx_club_account_transactions_account_date
  ON club_account_transactions (account_id, booking_date DESC);

CREATE INDEX IF NOT EXISTS idx_club_account_transactions_invoice
  ON club_account_transactions (invoice_id);

COMMIT;
