BEGIN;

DO $$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'group_join_policy') THEN
    CREATE TYPE group_join_policy AS ENUM (
      'NOT_ACCEPTING',
      'AUTO_ACCEPT',
      'APPROVAL_REQUIRED'
    );
  END IF;
END
$$;

DO $$
BEGIN
  IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'group_join_request_status') THEN
    CREATE TYPE group_join_request_status AS ENUM (
      'PENDING',
      'APPROVED',
      'DENIED',
      'CANCELED'
    );
  END IF;
END
$$;

CREATE TABLE IF NOT EXISTS group_settings (
  group_id INTEGER PRIMARY KEY REFERENCES households(id) ON DELETE CASCADE,
  join_policy group_join_policy NOT NULL DEFAULT 'NOT_ACCEPTING',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

INSERT INTO group_settings (group_id, join_policy)
SELECT h.id, 'NOT_ACCEPTING'::group_join_policy
FROM households h
WHERE NOT EXISTS (
  SELECT 1 FROM group_settings gs WHERE gs.group_id = h.id
);

CREATE TABLE IF NOT EXISTS group_join_requests (
  id SERIAL PRIMARY KEY,
  group_id INTEGER NOT NULL REFERENCES households(id) ON DELETE CASCADE,
  user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  status group_join_request_status NOT NULL DEFAULT 'PENDING',
  decided_by INTEGER REFERENCES users(id) ON DELETE SET NULL,
  decided_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE UNIQUE INDEX IF NOT EXISTS uq_group_join_requests_pending
  ON group_join_requests(group_id, user_id)
  WHERE status = 'PENDING';

CREATE INDEX IF NOT EXISTS idx_group_join_requests_group
  ON group_join_requests(group_id);

CREATE TABLE IF NOT EXISTS group_invite_links (
  id SERIAL PRIMARY KEY,
  group_id INTEGER NOT NULL REFERENCES households(id) ON DELETE CASCADE,
  created_by INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  token VARCHAR(64) NOT NULL UNIQUE,
  policy group_join_policy NOT NULL DEFAULT 'NOT_ACCEPTING',
  single_use BOOLEAN NOT NULL DEFAULT FALSE,
  expires_at TIMESTAMPTZ NOT NULL,
  used_at TIMESTAMPTZ,
  revoked_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_group_invite_links_group_id
  ON group_invite_links(group_id);

CREATE TABLE IF NOT EXISTS group_audit_log (
  id SERIAL PRIMARY KEY,
  group_id INTEGER NOT NULL REFERENCES households(id) ON DELETE CASCADE,
  actor_user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
  actor_role VARCHAR(20),
  event_type VARCHAR(100) NOT NULL,
  request_id VARCHAR(128) NOT NULL,
  ip INET,
  user_agent TEXT,
  success BOOLEAN NOT NULL DEFAULT TRUE,
  error_code VARCHAR(100),
  metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_group_audit_group_created
  ON group_audit_log(group_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_group_audit_request_id
  ON group_audit_log(request_id);

DO $$
BEGIN
  IF NOT EXISTS (
    SELECT 1
    FROM pg_indexes
    WHERE schemaname = current_schema()
      AND tablename = 'households'
      AND indexdef ILIKE 'CREATE UNIQUE INDEX%'
      AND indexdef ILIKE '%(invite_code)%'
  ) THEN
    CREATE UNIQUE INDEX idx_households_invite_code_unique
      ON households(invite_code);
  END IF;
END
$$;

ALTER TABLE household_members
DROP CONSTRAINT IF EXISTS household_members_role_check;

UPDATE household_members
SET role = 'member'
WHERE role = 'user';

WITH ranked_admins AS (
  SELECT
    hm.id,
    ROW_NUMBER() OVER (
      PARTITION BY hm.household_id
      ORDER BY hm.joined_at ASC, hm.id ASC
    ) AS admin_rank
  FROM household_members hm
  WHERE hm.role = 'admin'
)
UPDATE household_members hm
SET role = CASE
  WHEN ra.admin_rank = 1 THEN 'owner'
  ELSE 'admin'
END
FROM ranked_admins ra
WHERE hm.id = ra.id;

WITH ownerless_households AS (
  SELECT h.id AS household_id
  FROM households h
  WHERE NOT EXISTS (
    SELECT 1
    FROM household_members hm
    WHERE hm.household_id = h.id
      AND hm.role = 'owner'
  )
),
first_member AS (
  SELECT DISTINCT ON (hm.household_id)
    hm.id,
    hm.household_id
  FROM household_members hm
  JOIN ownerless_households oh ON oh.household_id = hm.household_id
  ORDER BY hm.household_id, hm.joined_at ASC, hm.id ASC
)
UPDATE household_members hm
SET role = 'owner'
FROM first_member fm
WHERE hm.id = fm.id;

ALTER TABLE household_members
ADD CONSTRAINT household_members_role_check
CHECK (role IN ('owner', 'admin', 'member'));

COMMIT;
