-- ============================================================================
-- Teams Schema: Invitations Table
-- Manages team/organization invitations
-- ============================================================================

-- ============================================================================
-- INVITATIONS TABLE
-- Stores pending and processed invitations
-- ============================================================================

CREATE TABLE IF NOT EXISTS teams.invitations (
    -- Primary key
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Tenant relationship
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,

    -- Invitation details
    email VARCHAR(255) NOT NULL,
    token_hash VARCHAR(255) NOT NULL,

    -- Invitee info (pre-filled for registration)
    first_name VARCHAR(100),
    last_name VARCHAR(100),

    -- Role to assign upon acceptance
    role_id UUID REFERENCES rbac.roles(id) ON DELETE SET NULL,

    -- Department/position
    department VARCHAR(100),
    job_title VARCHAR(100),

    -- Invitation message
    personal_message TEXT,

    -- Status
    status VARCHAR(20) NOT NULL DEFAULT 'pending'
        CHECK (status IN ('pending', 'accepted', 'declined', 'expired', 'revoked')),

    -- Expiration
    expires_at TIMESTAMPTZ NOT NULL,

    -- Tracking
    sent_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    resent_count INTEGER NOT NULL DEFAULT 0,
    last_resent_at TIMESTAMPTZ,

    -- Response tracking
    responded_at TIMESTAMPTZ,
    accepted_user_id UUID REFERENCES users.users(id),

    -- Audit fields
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    invited_by UUID NOT NULL REFERENCES users.users(id),
    revoked_at TIMESTAMPTZ,
    revoked_by UUID REFERENCES users.users(id),

    -- Constraints
    CONSTRAINT uq_invitation_token UNIQUE (token_hash),
    CONSTRAINT uq_pending_invitation UNIQUE (tenant_id, email, status)
        WHERE status = 'pending'
);

-- ============================================================================
-- INDEXES
-- ============================================================================

CREATE INDEX IF NOT EXISTS idx_invitations_tenant_id ON teams.invitations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_invitations_email ON teams.invitations(email);
CREATE INDEX IF NOT EXISTS idx_invitations_token_hash ON teams.invitations(token_hash);
CREATE INDEX IF NOT EXISTS idx_invitations_status ON teams.invitations(status);
CREATE INDEX IF NOT EXISTS idx_invitations_expires_at ON teams.invitations(expires_at) WHERE status = 'pending';
CREATE INDEX IF NOT EXISTS idx_invitations_invited_by ON teams.invitations(invited_by);

-- ============================================================================
-- ROW LEVEL SECURITY
-- ============================================================================

ALTER TABLE teams.invitations ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only see invitations in their tenant
CREATE POLICY invitations_tenant_isolation ON teams.invitations
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- ============================================================================
-- TRIGGERS
-- ============================================================================

-- Auto-update updated_at timestamp
CREATE OR REPLACE FUNCTION teams.update_invitations_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_invitations_updated_at
    BEFORE UPDATE ON teams.invitations
    FOR EACH ROW
    EXECUTE FUNCTION teams.update_invitations_timestamp();

-- ============================================================================
-- VIEW: Invitations with details
-- ============================================================================

CREATE OR REPLACE VIEW teams.v_invitations AS
SELECT
    i.id,
    i.tenant_id,
    t.name AS tenant_name,
    i.email,
    i.first_name,
    i.last_name,
    i.role_id,
    r.name AS role_name,
    i.department,
    i.job_title,
    i.personal_message,
    i.status,
    i.expires_at,
    i.sent_at,
    i.resent_count,
    i.last_resent_at,
    i.responded_at,
    i.created_at,
    i.invited_by,
    inviter.email AS invited_by_email,
    inviter.display_name AS invited_by_name,
    CASE
        WHEN i.status = 'pending' AND i.expires_at < CURRENT_TIMESTAMP THEN true
        ELSE false
    END AS is_expired
FROM teams.invitations i
JOIN tenants.tenants t ON i.tenant_id = t.id
LEFT JOIN rbac.roles r ON i.role_id = r.id
LEFT JOIN users.users inviter ON i.invited_by = inviter.id;

-- ============================================================================
-- FUNCTION: Create invitation
-- ============================================================================

CREATE OR REPLACE FUNCTION teams.create_invitation(
    p_tenant_id UUID,
    p_email VARCHAR(255),
    p_invited_by UUID,
    p_role_id UUID DEFAULT NULL,
    p_first_name VARCHAR(100) DEFAULT NULL,
    p_last_name VARCHAR(100) DEFAULT NULL,
    p_department VARCHAR(100) DEFAULT NULL,
    p_job_title VARCHAR(100) DEFAULT NULL,
    p_personal_message TEXT DEFAULT NULL,
    p_expires_in_days INTEGER DEFAULT 7
)
RETURNS TABLE (
    invitation_id UUID,
    token VARCHAR(64)
) AS $$
DECLARE
    v_token VARCHAR(64);
    v_token_hash VARCHAR(255);
    v_invitation_id UUID;
BEGIN
    -- Generate random token
    v_token := encode(gen_random_bytes(32), 'hex');
    v_token_hash := encode(sha256(v_token::bytea), 'hex');

    -- Revoke any existing pending invitations for this email
    UPDATE teams.invitations
    SET status = 'revoked',
        revoked_at = CURRENT_TIMESTAMP,
        revoked_by = p_invited_by
    WHERE tenant_id = p_tenant_id
      AND email = p_email
      AND status = 'pending';

    -- Create new invitation
    INSERT INTO teams.invitations (
        tenant_id, email, token_hash, role_id,
        first_name, last_name, department, job_title,
        personal_message, expires_at, invited_by
    ) VALUES (
        p_tenant_id, p_email, v_token_hash, p_role_id,
        p_first_name, p_last_name, p_department, p_job_title,
        p_personal_message,
        CURRENT_TIMESTAMP + (p_expires_in_days || ' days')::interval,
        p_invited_by
    )
    RETURNING id INTO v_invitation_id;

    RETURN QUERY SELECT v_invitation_id, v_token;
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- FUNCTION: Accept invitation
-- ============================================================================

CREATE OR REPLACE FUNCTION teams.accept_invitation(
    p_token VARCHAR(64),
    p_user_id UUID
)
RETURNS TABLE (
    success BOOLEAN,
    tenant_id UUID,
    role_id UUID,
    message TEXT
) AS $$
DECLARE
    v_invitation teams.invitations%ROWTYPE;
    v_token_hash VARCHAR(255);
BEGIN
    v_token_hash := encode(sha256(p_token::bytea), 'hex');

    -- Find invitation
    SELECT * INTO v_invitation
    FROM teams.invitations
    WHERE token_hash = v_token_hash;

    -- Check if invitation exists
    IF NOT FOUND THEN
        RETURN QUERY SELECT false, NULL::UUID, NULL::UUID, 'Invalid invitation token';
        RETURN;
    END IF;

    -- Check if already processed
    IF v_invitation.status != 'pending' THEN
        RETURN QUERY SELECT false, NULL::UUID, NULL::UUID,
            'Invitation has already been ' || v_invitation.status;
        RETURN;
    END IF;

    -- Check if expired
    IF v_invitation.expires_at < CURRENT_TIMESTAMP THEN
        UPDATE teams.invitations
        SET status = 'expired', updated_at = CURRENT_TIMESTAMP
        WHERE id = v_invitation.id;

        RETURN QUERY SELECT false, NULL::UUID, NULL::UUID, 'Invitation has expired';
        RETURN;
    END IF;

    -- Accept invitation
    UPDATE teams.invitations
    SET status = 'accepted',
        responded_at = CURRENT_TIMESTAMP,
        accepted_user_id = p_user_id,
        updated_at = CURRENT_TIMESTAMP
    WHERE id = v_invitation.id;

    -- Add user to team
    PERFORM teams.add_team_member(
        v_invitation.tenant_id,
        p_user_id,
        'member',
        v_invitation.invited_by,
        v_invitation.id,
        v_invitation.department,
        v_invitation.job_title
    );

    -- Assign role if specified
    IF v_invitation.role_id IS NOT NULL THEN
        PERFORM rbac.assign_role_to_user(
            p_user_id,
            v_invitation.role_id,
            v_invitation.tenant_id,
            v_invitation.invited_by,
            true -- Make it primary role
        );
    END IF;

    RETURN QUERY SELECT true, v_invitation.tenant_id, v_invitation.role_id,
        'Invitation accepted successfully';
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- FUNCTION: Resend invitation
-- ============================================================================

CREATE OR REPLACE FUNCTION teams.resend_invitation(
    p_invitation_id UUID,
    p_resent_by UUID
)
RETURNS TABLE (
    success BOOLEAN,
    token VARCHAR(64),
    message TEXT
) AS $$
DECLARE
    v_invitation teams.invitations%ROWTYPE;
    v_new_token VARCHAR(64);
    v_new_token_hash VARCHAR(255);
BEGIN
    -- Find invitation
    SELECT * INTO v_invitation
    FROM teams.invitations
    WHERE id = p_invitation_id;

    IF NOT FOUND THEN
        RETURN QUERY SELECT false, NULL::VARCHAR(64), 'Invitation not found';
        RETURN;
    END IF;

    IF v_invitation.status != 'pending' THEN
        RETURN QUERY SELECT false, NULL::VARCHAR(64),
            'Cannot resend: invitation is ' || v_invitation.status;
        RETURN;
    END IF;

    -- Generate new token
    v_new_token := encode(gen_random_bytes(32), 'hex');
    v_new_token_hash := encode(sha256(v_new_token::bytea), 'hex');

    -- Update invitation
    UPDATE teams.invitations
    SET token_hash = v_new_token_hash,
        resent_count = resent_count + 1,
        last_resent_at = CURRENT_TIMESTAMP,
        expires_at = CURRENT_TIMESTAMP + INTERVAL '7 days',
        updated_at = CURRENT_TIMESTAMP
    WHERE id = p_invitation_id;

    RETURN QUERY SELECT true, v_new_token, 'Invitation resent successfully';
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- FUNCTION: Expire old invitations (for scheduled job)
-- ============================================================================

CREATE OR REPLACE FUNCTION teams.expire_old_invitations()
RETURNS INTEGER AS $$
DECLARE
    v_count INTEGER;
BEGIN
    UPDATE teams.invitations
    SET status = 'expired', updated_at = CURRENT_TIMESTAMP
    WHERE status = 'pending'
      AND expires_at < CURRENT_TIMESTAMP;

    GET DIAGNOSTICS v_count = ROW_COUNT;
    RETURN v_count;
END;
$$ LANGUAGE plpgsql;

-- ============================================================================
-- GRANTS
-- ============================================================================

GRANT SELECT, INSERT, UPDATE, DELETE ON teams.invitations TO trading_user;
GRANT SELECT ON teams.v_invitations TO trading_user;
GRANT EXECUTE ON FUNCTION teams.create_invitation TO trading_user;
GRANT EXECUTE ON FUNCTION teams.accept_invitation TO trading_user;
GRANT EXECUTE ON FUNCTION teams.resend_invitation TO trading_user;
GRANT EXECUTE ON FUNCTION teams.expire_old_invitations TO trading_user;

-- ============================================================================
-- COMMENTS
-- ============================================================================

COMMENT ON TABLE teams.invitations IS 'Team/organization invitations for new members';
COMMENT ON COLUMN teams.invitations.token_hash IS 'SHA256 hash of the invitation token';
COMMENT ON COLUMN teams.invitations.role_id IS 'Role to assign when invitation is accepted';
COMMENT ON COLUMN teams.invitations.resent_count IS 'Number of times invitation was resent';
COMMENT ON VIEW teams.v_invitations IS 'Invitations with related details';
COMMENT ON FUNCTION teams.create_invitation IS 'Create a new team invitation';
COMMENT ON FUNCTION teams.accept_invitation IS 'Accept an invitation and join team';
