package ru.yandex.autotests.direct.db.steps;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.sql.Timestamp;
import java.util.List;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;

import org.jooq.impl.DSL;

import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.AdditionsItemCalloutsStatusmoderate;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannerPermalinksPermalinkAssignType;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.BannersAdditionsAdditionsType;
import ru.yandex.autotests.direct.db.models.jooq.ppc.enums.OrganizationsStatusPublish;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.AdditionsItemCalloutsRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.AdditionsItemDisclaimersRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.AdditionsItemExperimentsRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.BannersAdditionsRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.ClientPhonesRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.ModerateAdditionsRecord;
import ru.yandex.autotests.direct.db.models.jooq.ppc.tables.records.OrganizationsRecord;
import ru.yandex.autotests.direct.db.steps.base.BasePpcSteps;
import ru.yandex.autotests.direct.db.steps.base.DirectDbStepsException;
import ru.yandex.autotests.irt.testutils.RandomUtils;
import ru.yandex.qatools.allure.annotations.Step;

import static ru.yandex.autotests.direct.db.models.jooq.ppc.Tables.BANNER_PERMALINKS;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.Tables.BANNER_PHONES;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.Tables.CLIENT_PHONES;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.Tables.ORGANIZATIONS;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.tables.AdditionsItemCallouts.ADDITIONS_ITEM_CALLOUTS;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.tables.AdditionsItemDisclaimers.ADDITIONS_ITEM_DISCLAIMERS;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.tables.AdditionsItemExperiments.ADDITIONS_ITEM_EXPERIMENTS;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.tables.BannersAdditions.BANNERS_ADDITIONS;
import static ru.yandex.autotests.direct.db.models.jooq.ppc.tables.ModerateAdditions.MODERATE_ADDITIONS;


/*
* todo javadoc
*/
public class BannerAdditionsSteps extends BasePpcSteps {
    @Step
    public List<AdditionsItemCalloutsRecord> getClientCallouts(Long clientId) {
        return exec(db -> db.selectFrom(ADDITIONS_ITEM_CALLOUTS)
                .where(ADDITIONS_ITEM_CALLOUTS.CLIENTID.eq(clientId))
                .fetch()
        );
    }

    @Step("DB: чтение записи из ppc.additions_item_callouts по additions_item_id: {0}")
    public AdditionsItemCalloutsRecord getCallout(Long additionItemId) {
        return exec(db -> db.selectFrom(ADDITIONS_ITEM_CALLOUTS)
                .where(ADDITIONS_ITEM_CALLOUTS.ADDITIONS_ITEM_ID.eq(additionItemId))
                .fetchOne()
        );
    }

    @Step("DB: чтение записи из ppc.additions_item_callouts по bid: {0}")
    public List<AdditionsItemCalloutsRecord> getBannerCallouts(Long bid) {
        return exec(db -> db.select()
                .from(ADDITIONS_ITEM_CALLOUTS)
                .join(BANNERS_ADDITIONS)
                .on(ADDITIONS_ITEM_CALLOUTS.ADDITIONS_ITEM_ID.eq(BANNERS_ADDITIONS.ADDITIONS_ITEM_ID))
                .where(BANNERS_ADDITIONS.BID.eq(bid))
                .fetchInto(AdditionsItemCalloutsRecord.class));
    }

    @Step("DB: чтение записи из ppc.additions_item_disclaimer по additions_item_id: {0}")
    public AdditionsItemDisclaimersRecord getDisclaimer(Long additionItemId) {
        return exec(db -> db.selectFrom(ADDITIONS_ITEM_DISCLAIMERS)
                .where(ADDITIONS_ITEM_DISCLAIMERS.ADDITIONS_ITEM_ID.eq(additionItemId))
                .fetchOne()
        );
    }

    @Step("DB: чтение записей из ppc.additions_item_disclaimer по additions_item_id: {0}")
    public List<AdditionsItemDisclaimersRecord> getDisclaimers(Long... additionItemIds) {
        return exec(db -> db.selectFrom(ADDITIONS_ITEM_DISCLAIMERS)
                .where(ADDITIONS_ITEM_DISCLAIMERS.ADDITIONS_ITEM_ID.in(additionItemIds))
                .fetch()
        );
    }

    @Step("DB: чтение записей из ppc.banners_additions по bids: {0}")
    public List<BannersAdditionsRecord> getBannersAdditionsRecordsByBids(Long[] bids) {
        if (bids.length == 0) {
            throw new DirectDbStepsException("Нужно указать хотя бы 1н id баннера");
        }
        return exec(db -> db.selectFrom(BANNERS_ADDITIONS)
                .where(BANNERS_ADDITIONS.BID.in(bids))
                .fetch()
        );
    }

    @Step("DB: чтение записей из ppc.banners_additions по bid: {0}")
    public List<BannersAdditionsRecord> getBannersAdditionsRecordsByBid(Long bid) {
        return getBannersAdditionsRecordsByBids(new Long[]{bid});
    }

    //Step
    /*
    public List<BannersAdditionsRecord> getBannersAdditionsRecords(Long... bids) {
        if (bids.length == 0) {
            throw new DirectDbStepsException("Нужно указать хотя бы 1н id баннера");
        }
        int shard = shardingSteps().getShardByClientID(bids[0]);

        return exec("получаем записи banners_additions для bids " + Arrays.toString(bids), db ->
                db.selectFrom(BANNERS_ADDITIONS)
                        .where(BANNERS_ADDITIONS.BID.in(bids))
                        .fetch()
        );
    }*/

    public List<BannersAdditionsRecord> getBannersAdditionsRecords(Long clientId) {
        return exec("получаем записи banners_additions для clientId " + clientId,
                db -> db.selectFrom(BANNERS_ADDITIONS)
                        .where(BANNERS_ADDITIONS.ADDITIONS_ITEM_ID.in(
                                db.select(ADDITIONS_ITEM_CALLOUTS.ADDITIONS_ITEM_ID)
                                        .from(ADDITIONS_ITEM_CALLOUTS)
                                        .where(ADDITIONS_ITEM_CALLOUTS.CLIENTID.eq(clientId)))
                        ).fetch());
    }

    @Step("DB: очищаем ppc.banners_additions и ppc.additionsItemCallout для клиента {0}")
    public void clearCalloutsForClient(Long clientId) {
        List<Long> ba = getBannersAdditionsRecords(clientId)
                .stream()
                .map(BannersAdditionsRecord::getAdditionsItemId)
                .collect(Collectors.toList());

        run(db -> db.transaction(conf -> {
            if (ba.size() != 0) {
                DSL.using(conf).deleteFrom(BANNERS_ADDITIONS)
                        .where(BANNERS_ADDITIONS.ADDITIONS_ITEM_ID.in(ba))
                        .execute();
            }
            DSL.using(conf).deleteFrom(ADDITIONS_ITEM_CALLOUTS)
                    .where(ADDITIONS_ITEM_CALLOUTS.CLIENTID.in(clientId))
                    .execute();
        }));
    }

    @Step("Выставление статуса модерации {1} для уточнения c id = {0}")
    public void setAdditionsItemCalloutsStatusModerated(Long calloutId,
                                                        AdditionsItemCalloutsStatusmoderate statusModerated) {
        run(db -> db.update(ADDITIONS_ITEM_CALLOUTS)
                .set(ADDITIONS_ITEM_CALLOUTS.STATUSMODERATE, statusModerated)
                .where(ADDITIONS_ITEM_CALLOUTS.ADDITIONS_ITEM_ID
                        .eq(calloutId))
                .execute());
    }

    @Step("Обновление поля last_change {1} для уточнения c id = {0}")
    public void setAdditionsItemCalloutsLastChange(Long calloutId,
            Timestamp lastChange) {
        run(db -> db.update(ADDITIONS_ITEM_CALLOUTS)
                .set(ADDITIONS_ITEM_CALLOUTS.LAST_CHANGE, lastChange)
                .where(ADDITIONS_ITEM_CALLOUTS.ADDITIONS_ITEM_ID
                        .eq(calloutId))
                .execute());
    }

    @Step("DB: Oбновление записи в таблице ppc.additions_item_callouts")
    public void updateAdditionsItemCallouts(AdditionsItemCalloutsRecord additionsItemCallout) {
        run(db -> db.update(ADDITIONS_ITEM_CALLOUTS)
                .set(additionsItemCallout)
                .where(ADDITIONS_ITEM_CALLOUTS.ADDITIONS_ITEM_ID.eq(additionsItemCallout.getAdditionsItemId()))
                .execute()
        );
    }

    @Step("DB: Получаем записи по дополнениям для отправки на модерацию")
    public List<ModerateAdditionsRecord> getModerateAditions(Long itemId) {
        return exec(db -> db.selectFrom(MODERATE_ADDITIONS)
                .where(MODERATE_ADDITIONS.ADDITIONS_ITEM_ID.eq(itemId))
                .fetch()
        );
    }

    @Step("DB: создание записи в таблице ppc.additions_item_callouts")
    public AdditionsItemCalloutsRecord saveAdditionsItemCallouts(AdditionsItemCalloutsRecord additionsItemCalloutsRecord) {
        return exec(db -> db.insertInto(ADDITIONS_ITEM_CALLOUTS)
                .set(additionsItemCalloutsRecord)
                .returning()
                .fetchOne());
    }

    @Step("DB: создание записи в таблице ppc.banners_additions")
    public void saveBannerAdditions(BannersAdditionsRecord bannersAdditionsRecord) {
        run(db -> db.insertInto(BANNERS_ADDITIONS)
                .set(bannersAdditionsRecord)
                .execute());
    }

    @Step("DB: создание записи в таблице ppc.banners_additions bid:{0}, addition_item_id: {1}, type: {2}")
    public void saveBannerAdditions(Long bid, Long additionItemId, BannersAdditionsAdditionsType type) {
        saveBannerAdditions(new BannersAdditionsRecord()
                .setBid(bid)
                .setAdditionsItemId(additionItemId)
                .setAdditionsType(type)
                .setSequenceNum((short) 0));
    }


    @Step("DB: удаление записи из таблицы ppc.banners_additions, bid: {0}, additions_item_id: {0}")
    public void deleteBannerAdditions(Long bid, Long additionItemId) {
        run(db -> db.deleteFrom(BANNERS_ADDITIONS)
                .where(BANNERS_ADDITIONS.BID.eq(bid)
                        .and(BANNERS_ADDITIONS.ADDITIONS_ITEM_ID.eq(additionItemId)))
                .execute());
    }

    @Step("DB: создание записи в таблице ppc.additions_item_disclaimers")
    public void saveAdditionsItemDisclaimers(AdditionsItemDisclaimersRecord additionsItemDisclaimersRecord) {
        run(db -> db.insertInto(ADDITIONS_ITEM_DISCLAIMERS)
                .set(additionsItemDisclaimersRecord)
                .execute());
    }

    @Step("DB: создание записи в таблице ppc.additions_item_disclaimers, client_id: {0}, disclaimer_text: {1}")
    public Long saveAdditionsItemDisclaimers(Long clientId, String disclaimer) {
        Long additionItemId = autoIncSteps().getNewAdditionsItemId();
        saveAdditionsItemDisclaimers(new AdditionsItemDisclaimersRecord()
                .setAdditionsItemId(additionItemId)
                .setClientid(clientId)
                .setDisclaimerText(disclaimer)
                .setHash(BigInteger.valueOf(RandomUtils.getNextInt(2000000))));
        return additionItemId;
    }

    @Step("DB: удаление записи из таблицы ppc.additions_item_disclaimers, additions_item_id = {0}")
    public void deleteAdditionsItemDisclaimers(Long additionItemId) {
        run(db -> db.deleteFrom(ADDITIONS_ITEM_DISCLAIMERS)
                .where(ADDITIONS_ITEM_DISCLAIMERS.ADDITIONS_ITEM_ID.eq(additionItemId))
                .execute());
    }

    @Step("DB: добавление записи в таблицу ppc.additions_item_callouts (clientId = {0}, text = {1}, statusModerate = {2})")
    public Long saveAdditionsItemCallouts(Long clientId, String text, AdditionsItemCalloutsStatusmoderate status) {
        Long id = autoIncSteps().getNewAdditionsItemId();
        AdditionsItemCalloutsRecord additionsItemCalloutsRecord = new AdditionsItemCalloutsRecord()
                .setAdditionsItemId(id)
                .setCalloutText(text)
                .setClientid(clientId)
                .setHash(BigInteger.valueOf(RandomUtils.getNextInt(2000000)))
                .setStatusmoderate(status);
        saveAdditionsItemCallouts(additionsItemCalloutsRecord);
        return id;
    }

    @Step("DB: создание записи в таблице ppc.additions_item_experiments")
    public void saveAdditionsItemExperiments(AdditionsItemExperimentsRecord additionsItemExperimentsRecord) {
        run(db -> db.insertInto(ADDITIONS_ITEM_EXPERIMENTS)
                .set(additionsItemExperimentsRecord)
                .execute());
    }

    @Step("DB: удаление записи из таблицы ppc.additions_item_experiments, additions_item_id = {0}")
    public void deleteAdditionsItemExperiments(Long additionItemId) {
        run(db -> db.deleteFrom(ADDITIONS_ITEM_EXPERIMENTS)
                .where(ADDITIONS_ITEM_EXPERIMENTS.ADDITIONS_ITEM_ID.eq(additionItemId))
                .execute());
    }

    @Step("DB: создание записи в таблице ppc.additions_item_experiments, client_id: {0}, experiment_json: {1}")
    public Long saveAdditionsItemExperiments(Long clientId, String experiment_json) {
        Long additionItemId = autoIncSteps().getNewAdditionsItemId();
        saveAdditionsItemExperiments(new AdditionsItemExperimentsRecord()
                .setAdditionsItemId(additionItemId)
                .setClientid(clientId)
                .setExperimentJson(experiment_json.getBytes(StandardCharsets.UTF_8))
                .setHash(BigInteger.valueOf(RandomUtils.getNextInt(2000000))));
        return additionItemId;
    }

    @Step("DB: Добавление в записи в ppc.banner_permalinks: permalink = {1}, chain_id = {2} для bid = {0}")
    public void setPermalink(Long bid, Long permalink, Long chainId) {
        setPermalink(bid, permalink, chainId, null);
    }

    @Step("DB: Добавление в записи в ppc.banner_permalinks: permalink = {1}, chain_id = {2}, permalink_assign_type = " +
            "{3} для bid = {0}")
    public void setPermalink(Long bid, Long permalink, Long chainId, BannerPermalinksPermalinkAssignType type) {
        setPermalink(bid, permalink, chainId, type, false);
    }

    @Step("DB: Добавление записи в ppc.banner_phones: phone = {1} для bid = {0}")
    public void setPhone(Long bid, Long phoneId) {
        run(db -> db.insertInto(BANNER_PHONES)
                .set(BANNER_PHONES.BID, bid)
                .set(BANNER_PHONES.CLIENT_PHONE_ID, phoneId)
                .execute()
        );
    }

    @Step("DB: Добавление в записи в ppc.banner_permalinks: permalink = {1}, chain_id = {2}, " +
            "permalink_assign_type = {3}, is_sent_to_bs = {4} для bid = {0}")
    public void setPermalink(Long bid, Long permalink, Long chainId, BannerPermalinksPermalinkAssignType type,
                             boolean isSentToBs) {
        run(db -> db.insertInto(BANNER_PERMALINKS)
                .set(BANNER_PERMALINKS.PERMALINK, permalink)
                .set(BANNER_PERMALINKS.CHAIN_ID,chainId == null ? 0 : chainId)
                .set(BANNER_PERMALINKS.BID, bid)
                .set(BANNER_PERMALINKS.PERMALINK_ASSIGN_TYPE, type)
                .set(BANNER_PERMALINKS.IS_SENT_TO_BS, isSentToBs ? 1 : 0)
                .execute()
        );
    }

    @Step("DB: Обновление флага prefer_vcard_over_permalink в ppc.banner_permalinks")
    public void updatePreferVCardOverPermalink(Long bid, boolean preferVCardOverPermalink) {
        run(db -> db.update(BANNER_PERMALINKS)
                .set(BANNER_PERMALINKS.PREFER_VCARD_OVER_PERMALINK, preferVCardOverPermalink ? 1 : 0)
                .where(BANNER_PERMALINKS.BID.eq(bid))
                .and(BANNER_PERMALINKS.PERMALINK_ASSIGN_TYPE.eq(BannerPermalinksPermalinkAssignType.manual))
                .execute()
        );
    }

    @Step("DB: Обновление флага is_sent_to_bs в ppc.banner_permalinks")
    public void updatePermalinkIsSentToBs(Long permalink, boolean isSentToBs) {
        run(db -> db.update(BANNER_PERMALINKS)
                .set(BANNER_PERMALINKS.IS_SENT_TO_BS, isSentToBs ? 1 : 0)
                .where(BANNER_PERMALINKS.PERMALINK.eq(permalink))
                .execute()
        );
    }
    @Step("DB: Удаление записи в ppc.banner_permalinks: permalink = {1}, chain_id = {2}, permalink_assign_type = " +
            "{3} для bid = {0}")
    public void deletePermalink(Long bid, Long permalink, Long chainId, BannerPermalinksPermalinkAssignType type) {
        run(db -> db.deleteFrom(BANNER_PERMALINKS)
                .where(BANNER_PERMALINKS.PERMALINK.eq(permalink))
                .and(BANNER_PERMALINKS.CHAIN_ID.eq(chainId == null ? 0 : chainId))
                .and(BANNER_PERMALINKS.BID.eq(bid))
                .and(BANNER_PERMALINKS.PERMALINK_ASSIGN_TYPE.eq(type))
                .execute());
    }

    @Step("DB: Удаление записи в ppc.banner_permalinks: permalink_assign_type = {1} для bid = {0}")
    public void deletePermalinkByBidAndType(Long bid, BannerPermalinksPermalinkAssignType type) {
        run(db -> db.deleteFrom(BANNER_PERMALINKS)
                .where(BANNER_PERMALINKS.BID.eq(bid))
                .and(BANNER_PERMALINKS.PERMALINK_ASSIGN_TYPE.eq(type))
                .execute());
    }

    @Step("DB: Добавление и обновление организации в ppc.organizations")
    public void addOrUpdateOrganization(OrganizationsRecord record) {
        run(db -> db.insertInto(ORGANIZATIONS)
                .set(record)
                .onDuplicateKeyUpdate()
                .set(ORGANIZATIONS.CHAIN_ID, record.getChainId())
                .set(ORGANIZATIONS.STATUS_PUBLISH, record.getStatusPublish())
                .execute());
    }

    @Step("DB: Обновление статуса публикации организации в ppc.organizations")
    public void updateOrganizationStatusPublish(@Nonnull Long permalink, OrganizationsStatusPublish status) {
        run(db -> db.update(ORGANIZATIONS)
                .set(ORGANIZATIONS.STATUS_PUBLISH, status)
                .where(ORGANIZATIONS.PERMALINK_ID.eq(permalink))
                .execute());
    }

    @Step("DB: Добавление подменного телефона организации в ppc.client_phones")
    public void addClientPhone(ClientPhonesRecord record) {
        run(db -> db.insertInto(CLIENT_PHONES)
                .set(record)
                .execute());
    }

    @Step("DB: Удаление подменного телефона организации в ppc.client_phones")
    public void deleteClientPhone(Long phoneId) {
        run(db -> db.deleteFrom(CLIENT_PHONES)
                .where(CLIENT_PHONES.CLIENT_PHONE_ID.eq(phoneId))
                .execute());
    }
}
