import { callUnomi, callElasticsearch } from "../utils/connection";
import { validateRequiredProps } from "../utils/validation";
import { queryBuilder } from "../queryBuilder/profileGetByProperty";
import { CreateProperties, ExistingProperties, GetByProperty, QueryConfig } from "../../src/types/profiles";
import { FilteredResponse } from "../../src/types/sdkResponse";
import { QueryParams } from "../../src/types/queryBuilder"

const defaultProperties: CreateProperties = {
    consents: {},
    itemId: undefined,
    itemType: "profile",
    mergedWith: null,
    properties: undefined,
    scores: {},
    segments: [],
    systemProperties: {},
    version: null
};

/**
 * @function get
 * @param {string} baseUrl 
 * @param {Record<string, string>} headers 
 * @param {string} profileId
 * @returns {FilteredResponse}
 */
export function get(baseUrl: string, headers: Record<string, string>, profileId: string): FilteredResponse {

    if (!profileId) {
        throw new Error(`profileId argument is missing, null or undefined.`);
    }

    const url = baseUrl + `/cxs/profiles/${profileId}`;

    return callUnomi("GET", url, null, headers, 200);
}

/**
 * @function create
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @param {CreateProperties} properties
 * @returns {FilteredResponse}
 */
export function create(baseUrl: string, auth: Record<string, string>, properties: CreateProperties): FilteredResponse {
    const requiredProperties = ["itemId", "properties"];
    const propsValidation = validateRequiredProps(requiredProperties, properties);

    if (!propsValidation.valid) {
        throw new Error(`The following properties are missing, null or undefined: ${propsValidation.missing.join(',')}`);
    }

    const url = baseUrl + `/cxs/profiles`;

    return callUnomi("POST", url, { ...defaultProperties, ...properties }, auth, 204);
}

/**
 * @function delete
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @param {string} profileId
 * @returns {FilteredResponse}
 */
export function deleteProfile(baseUrl: string, auth: Record<string, string>, profileId: string): FilteredResponse {

    if (!profileId) {
        throw new Error(`profileId argument is missing, null or undefined.`);
    }

    const url = baseUrl + `/cxs/profiles/${profileId}`;

    return callUnomi("DELETE", url, null, auth, 204);
}

/**
 * @function count
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @returns {FilteredResponse}
 */
export function count(baseUrl: string, auth: Record<string, string>): FilteredResponse {
    const url = baseUrl + `/cxs/profiles/count`;

    return callUnomi("GET", url, null, auth, 200);
}

/**
 * @function existingProperties
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @param {ExistingProperties} params
 * @returns {FilteredResponse}
 */
export function existingProperties(baseUrl: string, auth: Record<string, string>, params: ExistingProperties): FilteredResponse {
    const requiredProperties = ["tag", "itemType"];
    const propsValidation = validateRequiredProps(requiredProperties, params);

    if (!propsValidation.valid) {
        throw new Error(`The following properties are missing, null or undefined: ${propsValidation.missing.join(',')}`);
    }

    const urlSearchParams = new URLSearchParams(Object.entries(params));
    const url = baseUrl + `/cxs/profiles/existingProperties?${urlSearchParams.toString()}`;

    return callUnomi("GET", url, null, auth, 200);
}

/**
 * @function allProperties
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @returns {FilteredResponse}
 */
export function allProperties(baseUrl: string, auth: Record<string, string>): FilteredResponse {
    const url = baseUrl + `/cxs/profiles/properties`;

    return callUnomi("GET", url, null, auth, 200);
}

/**
 * @function sessions
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @param {FilteredResponse} profileId
 * @returns {FilteredResponse}
 */
export function sessions(baseUrl: string, auth: Record<string, string>, profileId: string): FilteredResponse {

    if (!profileId) {
        throw new Error(`Profile ID is not valid. Received: ${profileId}`);
    }

    const url = baseUrl + `/cxs/profiles/${profileId}/sessions`;

    return callUnomi("GET", url, null, auth, 200);
}

/**
 * @function getByProperty
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @param {GetByProperty} params
 * @returns {FilteredResponse}
 */
export function getBySingleProperty(baseUrl: string, auth: Record<string, string>, params: GetByProperty): FilteredResponse {
    const requiredProperties = ["query", "limit"];
    const propsValidation = validateRequiredProps(requiredProperties, params);

    if (!propsValidation.valid) {
        throw new Error(`The following properties are missing, null or undefined: ${propsValidation.missing.join(',')}`);
    }

    const query = queryBuilder(params.query);

    const queryparam = {
        offset: params.offset || 0,
        limit: params.limit || 100,
        condition: {
            type: "profilePropertyCondition",
            parameterValues: {
                propertyName: `properties.${query.key}`,
                comparisonOperator: query.operator,
                propertyValue: query.value
            }
        },
        forceRefresh: params.forceRefresh || false
    }

    const url = baseUrl + `cxs/profiles/search`;

    return callUnomi("POST", url, queryparam, auth, 200);
}

/**
 * @function query
 * @param {string} baseUrl 
 * @param {Record<string, string>} auth 
 * @param {GetByProperty} params
 * @param {QueryParams[]} query
 * @returns {FilteredResponse}
 */
export function query(baseUrl: string, auth: Record<string, string>, params: QueryConfig, query: QueryParams[]): FilteredResponse {

    const subConditions = query.map((queryCond: QueryParams) => {
        return {
            type: "profilePropertyCondition",
            parameterValues: {
                propertyName: queryCond.prop,
                comparisonOperator: queryCond.operator,
                propertyValue: queryCond.value
            }
        }
    });

    const fullQuery = {
        offset: params.offset || 0,
        limit: params.limit || 100,
        condition: {
            type: "booleanCondition",
            parameterValues: {
                operator: params.operator || "and",
                subConditions
            }
        },
        forceRefresh: params.forceRefresh || true
    }

    const url = baseUrl + `cxs/profiles/search`;

    return callUnomi("POST", url, fullQuery, auth, 200);
}

/**
 * @function allUsedProperties
 * @param {string} baseUrl 
 * @param {Record<string, string>} headers 
 * @returns {FilteredResponse}
 */
export function allUsedProperties(baseUrl: string, headers: Record<string, string>): FilteredResponse { // get all used profile properties
    const url = baseUrl + `/context-profile`; // profile endpoint

    return callElasticsearch("GET", url, null, headers, 200); // return result object
}