CREATE EXTENSION IF NOT EXISTS pg_trgm;

CREATE TYPE clientgender AS ENUM ('MALE', 'FEMALE');
CREATE TYPE orderevent AS ENUM ('CREATED', 'REJECTED', 'ACCEPTED');
CREATE TYPE call_event_type AS ENUM ('INITIATED', 'FINISHED');

CREATE TABLE clients (
    id bigserial PRIMARY KEY,
    biz_id bigint NOT NULL,
    first_name varchar(256) CHECK ( length(first_name) > 0 ),
    last_name varchar(256) CHECK ( length(last_name) > 0 ),
    passport_uid bigint,
    phone varchar(16) CHECK ( length(phone) > 0 ),
    email varchar(64) CHECK ( length(email) > 0 ),
    gender clientgender,
    comment text CHECK ( length(comment) > 0 ),
    created_at timestamptz DEFAULT NOW(),
    ts_storage tsvector,
    UNIQUE (biz_id, passport_uid)
);

CREATE INDEX clients_ts_storage_gin_idx
    ON clients
    USING gin (ts_storage);

CREATE INDEX clients_biz_id_hash_idx
    ON clients
    USING hash (biz_id);

CREATE INDEX clients_phone_gin_trgm_idx
    ON clients
    USING gin (phone gin_trgm_ops);

CREATE INDEX clients_email_gin_trgm_idx
    ON clients
    USING gin (email gin_trgm_ops);

CREATE UNIQUE INDEX clients_unique_biz_id_phone_and_passport_uid_is_null
    ON clients (phone, biz_id)
    WHERE passport_uid IS NULL;

CREATE UNIQUE INDEX clients_unique_biz_id_email_and_passport_uid_and_phone_is_null
    ON clients (email, biz_id)
    WHERE passport_uid IS NULL AND phone IS NULL;


CREATE TABLE client_revisions (
    id bigserial PRIMARY KEY,
    client_id bigint NOT NULL REFERENCES clients(id),
    biz_id bigint NOT NULL,
    source varchar(64) NOT NULL,
    metadata jsonb,
    first_name varchar(256) CHECK ( length(first_name) > 0 ),
    last_name varchar(256) CHECK ( length(last_name) > 0 ),
    passport_uid bigint,
    phone varchar(16) CHECK ( length(phone) > 0 ),
    email varchar(64) CHECK ( length(email) > 0 ),
    gender clientgender,
    comment text CHECK ( length(comment) > 0 ),
    initiator_id bigint,
    created_at timestamptz DEFAULT NOW()
);

CREATE INDEX client_revisions_biz_id_btree_idx
    ON client_revisions
    USING btree (biz_id);

CREATE INDEX client_revisions_client_id_ordered_by_created_at
    ON client_revisions
    USING btree (client_id, created_at);

-- Stores all words-tokens we are know
-- Are used for fuzzy search
-- Must be refill each time when searchable columns in clients table ara changed
CREATE TABLE search_tokens (
  id bigserial PRIMARY KEY,
  token text NOT NULL UNIQUE
);

CREATE INDEX search_tokens_token_gin_idx ON search_tokens
USING gin (token gin_trgm_ops);

CREATE TABLE order_events (
    id bigserial PRIMARY KEY,
    client_id bigint NOT NULL REFERENCES clients(id),
    biz_id bigint NOT NULL,
    event_type orderevent NOT NULL,
    event_timestamp timestamptz NOT NULL,
    source varchar(64) NOT NULL,
    created_at timestamptz DEFAULT NOW(),
    order_id bigint NOT NULL
);

CREATE INDEX order_events_biz_id_btree_index
    ON order_events
    USING btree (biz_id);

CREATE TABLE call_events (
    id bigserial PRIMARY KEY,
    client_id bigint NOT NULL REFERENCES clients(id),
    biz_id bigint NOT NULL,
    event_timestamp timestamptz NOT NULL,
    source varchar(64) NOT NULL,
    created_at timestamptz DEFAULT NOW(),
    event_type call_event_type NOT NULL DEFAULT 'INITIATED',
    event_value varchar(512),
    record_url varchar(512),
    session_id bigint,
    await_duration smallint,
    talk_duration smallint,
    geoproduct_id bigint
);

CREATE INDEX call_events_biz_id_btree_index
    ON call_events
    USING btree (biz_id);

CREATE INDEX call_events_geoproduct_id_btree_index
    ON call_events
    USING btree (geoproduct_id);

CREATE INDEX call_events_client_id_biz_id_btree_index
    ON call_events
    USING btree (client_id, biz_id);

CREATE TRIGGER clients_ts_storage_update
    BEFORE INSERT OR UPDATE
    ON clients FOR EACH ROW EXECUTE FUNCTION
    tsvector_update_trigger(
        ts_storage, 'pg_catalog.russian',
        first_name, last_name, phone, email, comment
    );

CREATE FUNCTION parse_tokens(text)
RETURNS TABLE(token text)
AS $$
    SELECT lexeme
    FROM unnest(to_tsvector('simple', $1))
$$ LANGUAGE SQL;

CREATE FUNCTION add_search_tokens()
RETURNS trigger AS $$
    BEGIN
        INSERT INTO search_tokens (token)
        SELECT * FROM parse_tokens(NEW.first_name)
        UNION
        SELECT * FROM parse_tokens(NEW.last_name)
        UNION
        SELECT NEW.phone WHERE NEW.phone IS NOT NULL
        UNION
        SELECT NEW.email WHERE NEW.email IS NOT NULL
        UNION
        SELECT * FROM parse_tokens(NEW.comment)
        ON CONFLICT DO nothing;
        RETURN NEW;
    END
$$ LANGUAGE 'plpgsql';

CREATE TRIGGER client_update_trigger
    BEFORE INSERT OR UPDATE
    ON clients FOR EACH ROW EXECUTE FUNCTION
    add_search_tokens();
