-- TAGS: Global (Shared across all projects)
CREATE TABLE tags (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  tag_code VARCHAR(10) NOT NULL UNIQUE, 
  tag_description TEXT NOT NULL
);

-- GROUPS: Global (Shared across all projects)
CREATE TABLE groups (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  group_name TEXT NOT NULL UNIQUE,
  hex_color VARCHAR(7) NOT NULL 
);

-- PRIORITIES: Global
CREATE TABLE priorities (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  priority_name TEXT NOT NULL UNIQUE,
  priority_num INT NOT NULL DEFAULT 0
);

-- VALIDATION STATUSES: Global (Approved, Rejected, etc.)
CREATE TABLE validation_statuses (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  status_name TEXT NOT NULL UNIQUE
);

-- [NEW] REQUIREMENT STATUSES: Global (Draft, Regular, Deprecated)
-- This allows you to manage lifecycle states dynamically.
CREATE TABLE requirement_statuses (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  status_code VARCHAR(20) NOT NULL UNIQUE, -- e.g. 'DRAFT'
  status_name TEXT NOT NULL,               -- e.g. 'Draft'
  description TEXT
);

-- Seed initial statuses so the FKs work immediately
INSERT INTO requirement_statuses (status_code, status_name, description) VALUES 
('DRAFT', 'Draft', 'Initial version, not ready for review'),
('REGULAR', 'Regular', 'Active requirement');

-- ROLES: System-wide roles (e.g., Admin, User)
CREATE TABLE roles (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  role_name TEXT NOT NULL UNIQUE
);

-- USERS: System-wide users
CREATE TABLE users (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  sub TEXT NOT NULL UNIQUE, -- Keycloak Subject ID
  username TEXT NOT NULL,
  full_name TEXT,
  role_id INT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  CONSTRAINT fk_users_role FOREIGN KEY (role_id) REFERENCES roles (id)
);

-- PROJECTS: The container for requirements
CREATE TABLE projects (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  project_name TEXT NOT NULL,
  project_desc TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- PROJECT_MEMBERS: Controls Access (Many-to-Many)
CREATE TABLE project_members (
  project_id INT NOT NULL,
  user_id INT NOT NULL,
  joined_at TIMESTAMPTZ DEFAULT NOW(),
  
  PRIMARY KEY (project_id, user_id),
  
  CONSTRAINT fk_pm_project FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
  CONSTRAINT fk_pm_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE
);

-- REQUIREMENTS: Updated with Status ID
CREATE TABLE requirements (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  project_id INT NOT NULL,
  user_id INT NOT NULL, 
  tag_id INT NOT NULL,
  status_id INT NOT NULL DEFAULT 1, -- [NEW] Defaults to ID 1 (Draft)
  last_editor_id INT,
  req_name TEXT NOT NULL,
  req_desc TEXT,
  priority_id INT,
  version INT NOT NULL DEFAULT 1, 
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  
  CONSTRAINT fk_req_project FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
  CONSTRAINT fk_req_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL,
  CONSTRAINT fk_req_editor FOREIGN KEY (last_editor_id) REFERENCES users (id),
  CONSTRAINT fk_req_priority FOREIGN KEY (priority_id) REFERENCES priorities (id),
  CONSTRAINT fk_req_tag FOREIGN KEY (tag_id) REFERENCES tags (id),
  CONSTRAINT fk_req_status FOREIGN KEY (status_id) REFERENCES requirement_statuses (id) -- [NEW]
);

-- REQUIREMENTS_GROUPS: Join table for M:N relationship
CREATE TABLE requirements_groups (
  requirement_id INT NOT NULL,
  group_id INT NOT NULL,
  
  PRIMARY KEY (requirement_id, group_id),
  
  CONSTRAINT fk_rg_req FOREIGN KEY (requirement_id) REFERENCES requirements (id) ON DELETE CASCADE,
  CONSTRAINT fk_rg_group FOREIGN KEY (group_id) REFERENCES groups (id) ON DELETE CASCADE
);

-- VALIDATIONS: No change
CREATE TABLE validations (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  requirement_id INT NOT NULL,
  user_id INT NOT NULL, 
  status_id INT NOT NULL,
  req_version_snapshot INT NOT NULL, 
  comment TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  
  CONSTRAINT fk_validation_req FOREIGN KEY (requirement_id) REFERENCES requirements (id) ON DELETE CASCADE,
  CONSTRAINT fk_validation_user FOREIGN KEY (user_id) REFERENCES users (id),
  CONSTRAINT fk_validation_status FOREIGN KEY (status_id) REFERENCES validation_statuses (id)
);

-- REQUIREMENTS_HISTORY: Updated to track 'status_id' changes
CREATE TABLE requirements_history (
  history_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  original_req_id INT NOT NULL, 
  project_id INT,
  status_id INT,          -- [NEW] Added to history
  req_name TEXT,
  req_desc TEXT,
  priority_id INT,
  tag_id INT,
  version INT, 
  valid_from TIMESTAMPTZ,
  valid_to TIMESTAMPTZ,
  edited_by INT           
);

-- The function to archive the old row before update
CREATE OR REPLACE FUNCTION archive_requirement_change()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO requirements_history (
    original_req_id, 
    project_id,
    status_id,      -- [NEW]
    req_name, 
    req_desc, 
    priority_id, 
    tag_id,
    version, 
    valid_from, 
    valid_to, 
    edited_by
  )
  VALUES (
    OLD.id,
    OLD.project_id,
    OLD.status_id,  -- [NEW]
    OLD.req_name,
    OLD.req_desc,
    OLD.priority_id,
    OLD.tag_id,
    OLD.version,
    OLD.updated_at, 
    NOW(),          
    OLD.last_editor_id 
  );
  
  NEW.version := OLD.version + 1; 
  NEW.updated_at := NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Bind Trigger to requirements
CREATE TRIGGER trigger_audit_requirements
BEFORE UPDATE ON requirements
FOR EACH ROW
EXECUTE FUNCTION archive_requirement_change();

-- Indexes for performance
CREATE INDEX idx_req_project ON requirements(project_id);
CREATE INDEX idx_req_tag ON requirements(tag_id);
CREATE INDEX idx_req_priority ON requirements(priority_id);
CREATE INDEX idx_req_user ON requirements(user_id);
CREATE INDEX idx_req_status ON requirements(status_id); -- [NEW]
CREATE INDEX idx_pm_user ON project_members(user_id);

-- RELATIONSHIP_TYPES: Defines valid connection types per project
CREATE TABLE relationship_types (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  project_id INT NOT NULL,
  type_name TEXT NOT NULL,
  type_description TEXT,
  inverse_type_name TEXT,
  
  CONSTRAINT fk_rel_type_project FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
  CONSTRAINT uq_rel_type_name_project UNIQUE (project_id, type_name)
);

-- REQUIREMENT_LINKS: The actual connections between requirements
CREATE TABLE requirement_links (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  source_req_id INT NOT NULL,
  target_req_id INT NOT NULL,
  relationship_type_id INT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  created_by INT,
  
  CONSTRAINT fk_link_source FOREIGN KEY (source_req_id) REFERENCES requirements (id) ON DELETE CASCADE,
  CONSTRAINT fk_link_target FOREIGN KEY (target_req_id) REFERENCES requirements (id) ON DELETE CASCADE,
  CONSTRAINT fk_link_type FOREIGN KEY (relationship_type_id) REFERENCES relationship_types (id) ON DELETE CASCADE,
  CONSTRAINT fk_link_creator FOREIGN KEY (created_by) REFERENCES users (id) ON DELETE SET NULL,

  CONSTRAINT check_no_self_link CHECK (source_req_id <> target_req_id), 
  CONSTRAINT uq_req_link_pair UNIQUE (source_req_id, target_req_id, relationship_type_id)
);

CREATE INDEX idx_link_source ON requirement_links(source_req_id);
CREATE INDEX idx_link_target ON requirement_links(target_req_id);

-- REQUIREMENT_COMMENTS: Top-level comments
CREATE TABLE requirement_comments (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  requirement_id INT NOT NULL,
  user_id INT, 
  comment_text TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  is_deleted BOOLEAN DEFAULT FALSE,
  
  CONSTRAINT fk_rc_req FOREIGN KEY (requirement_id) REFERENCES requirements (id) ON DELETE CASCADE,
  CONSTRAINT fk_rc_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL
);

-- REQUIREMENT_COMMENT_REPLIES: Responses to top-level comments
CREATE TABLE requirement_comment_replies (
  id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  parent_comment_id INT NOT NULL,
  user_id INT,
  reply_text TEXT NOT NULL,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  is_deleted BOOLEAN DEFAULT FALSE,

  CONSTRAINT fk_rcr_parent FOREIGN KEY (parent_comment_id) REFERENCES requirement_comments (id) ON DELETE CASCADE,
  CONSTRAINT fk_rcr_user FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE SET NULL
);

CREATE INDEX idx_rc_req ON requirement_comments(requirement_id);
CREATE INDEX idx_rcr_parent ON requirement_comment_replies(parent_comment_id);

-- 1. HISTORY TABLE: Captures deleted/changed links with Snapshots
CREATE TABLE requirement_links_history (
  history_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  original_link_id INT,           -- The ID of the link from the main table
  source_req_id INT,
  target_req_id INT,
  relationship_type_id INT,       -- Kept for reference
  relationship_type_snapshot TEXT, -- [IMPORTANT] Text copy of type name (e.g. "Depends On")
  inverse_type_snapshot TEXT,      -- [IMPORTANT] Text copy of inverse name (e.g. "Is Depended On By")
  created_by INT,                 
  valid_from TIMESTAMPTZ,         -- When the link was created
  valid_to TIMESTAMPTZ DEFAULT NOW() -- When the link was deleted/changed
);

-- Indexes for querying link history
CREATE INDEX idx_link_hist_source ON requirement_links_history(source_req_id);
CREATE INDEX idx_link_hist_target ON requirement_links_history(target_req_id);

-- 2. FUNCTION: Handles the archiving with Snapshot lookup
CREATE OR REPLACE FUNCTION archive_link_change()
RETURNS TRIGGER AS $$
DECLARE
  v_type_name TEXT;
  v_inverse_name TEXT;
BEGIN
  -- Fetch current names from the configuration table to snapshot them
  SELECT type_name, inverse_type_name INTO v_type_name, v_inverse_name
  FROM relationship_types 
  WHERE id = OLD.relationship_type_id;

  -- Insert into history with the text snapshots
  INSERT INTO requirement_links_history (
    original_link_id,
    source_req_id,
    target_req_id,
    relationship_type_id,
    relationship_type_snapshot,
    inverse_type_snapshot,
    created_by,
    valid_from,
    valid_to
  )
  VALUES (
    OLD.id,
    OLD.source_req_id,
    OLD.target_req_id,
    OLD.relationship_type_id,
    v_type_name,       -- Saved permanently
    v_inverse_name,    -- Saved permanently
    OLD.created_by,
    OLD.created_at,
    NOW()
  );

  IF (TG_OP = 'DELETE') THEN
    RETURN OLD;
  ELSE
    RETURN NEW;
  END IF;
END;
$$ LANGUAGE plpgsql;

-- 3. TRIGGER: Binds the function to the table
CREATE TRIGGER trigger_audit_links
BEFORE UPDATE OR DELETE ON requirement_links
FOR EACH ROW
EXECUTE FUNCTION archive_link_change();

-- 1. PRE-REQUISITE: Add created_at to the join table
-- We need this to know when the relationship began (valid_from).
ALTER TABLE requirements_groups 
ADD COLUMN created_at TIMESTAMPTZ DEFAULT NOW();

-- 2. HISTORY TABLE: Captures removed/changed group associations
CREATE TABLE requirements_groups_history (
  history_id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  requirement_id INT NOT NULL,
  group_id INT NOT NULL,
  
  group_name_snapshot TEXT, 
  group_hex_color_snapshot VARCHAR(7),
  
  valid_from TIMESTAMPTZ,         -- When the association was created
  valid_to TIMESTAMPTZ DEFAULT NOW() -- When the association was removed/changed
);

-- Index for querying history by requirement
CREATE INDEX idx_req_groups_hist_req ON requirements_groups_history(requirement_id);

-- 3. FUNCTION: Handles the archiving with Snapshot lookup
CREATE OR REPLACE FUNCTION archive_requirements_groups_change()
RETURNS TRIGGER AS $$
DECLARE
  v_group_name TEXT;
  v_hex_color VARCHAR(7);
BEGIN
  -- Fetch current group details to snapshot them
  -- We do this so the history remains readable even if the Group ID is deleted later
  SELECT group_name, hex_color INTO v_group_name, v_hex_color
  FROM groups 
  WHERE id = OLD.group_id;

  -- Insert into history
  INSERT INTO requirements_groups_history (
    requirement_id,
    group_id,
    group_name_snapshot,
    group_hex_color_snapshot,
    valid_from,
    valid_to
  )
  VALUES (
    OLD.requirement_id,
    OLD.group_id,
    v_group_name,    -- Saved permanently
    v_hex_color,     -- Saved permanently
    OLD.created_at,  -- The time it was originally linked
    NOW()            -- The time it was removed
  );

  IF (TG_OP = 'DELETE') THEN
    RETURN OLD;
  ELSE
    RETURN NEW;
  END IF;
END;
$$ LANGUAGE plpgsql;

-- 4. TRIGGER: Binds the function to the table
-- Triggers on DELETE (removing a group) or UPDATE (swapping a group ID directly)
CREATE TRIGGER trigger_audit_requirements_groups
BEFORE UPDATE OR DELETE ON requirements_groups
FOR EACH ROW
EXECUTE FUNCTION archive_requirements_groups_change();