"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var CronSyncService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CronSyncService = void 0;
const axios_1 = require("@nestjs/axios");
const common_1 = require("@nestjs/common");
const mongoose_1 = require("@nestjs/mongoose");
const mongoose_2 = require("mongoose");
const jwt_1 = require("@nestjs/jwt");
const CountryCodeToCurrency_1 = require("../constant/CountryCodeToCurrency");
const rxjs_1 = require("rxjs");
const AnalyticsReport_schema_1 = require("../models/AnalyticsReport.schema");
const moment = require("moment");
const fs = require("fs");
const zlib = require("zlib");
const csv_parse_1 = require("csv-parse");
const event_emitter_1 = require("@nestjs/event-emitter");
const AnalyticsReport_service_1 = require("./AnalyticsReport.service");
const CurrentStage_enum_1 = require("../enum/CurrentStage.enum");
const schedule_1 = require("@nestjs/schedule");
const logger_service_1 = require("../utils/logger.service");
let CronSyncService = CronSyncService_1 = class CronSyncService {
    constructor(CurrencyValuationRepo, analyticsReportRepo, AppleAccountRepo, analyticsService, eventEmitter, httpService, jwtService, LoggerService) {
        this.CurrencyValuationRepo = CurrencyValuationRepo;
        this.analyticsReportRepo = analyticsReportRepo;
        this.AppleAccountRepo = AppleAccountRepo;
        this.analyticsService = analyticsService;
        this.eventEmitter = eventEmitter;
        this.httpService = httpService;
        this.jwtService = jwtService;
        this.LoggerService = LoggerService;
        this.CONST_REPORT_TYPES = {
            SALES: 'SALES',
            DOWNLOAD: 'DOWNLOAD'
        };
        this.APPLEDATEFORMAT = "YYYY-MM-DD";
        this.DATEFORMAT = "DD-MM-YYYY";
        this.logger = new common_1.Logger(CronSyncService_1.name);
        this.doFetchVendors = async () => {
            return {
                data: await this.AppleAccountRepo.find({}, {
                    VendorID: true,
                    AccountName: true
                }),
                status: true
            };
        };
        this.generateJWT = async ({ privateKey, iss, KeyId }) => {
            const payload = { iss: iss, aud: process.env.audience };
            return {
                access_token: await this.jwtService.sign(payload, {
                    header: {
                        alg: process.env.alg,
                        kid: KeyId
                    },
                    privateKey: privateKey
                })
            };
        };
        this.doGetUSDBaseValue = async (Currency, amount) => {
            let data = await this.CurrencyValuationRepo.findOne({ currency: Currency }).sort({ "createdAt": -1 }).limit(1).exec();
            if (data) {
                return { value: data.rates * amount, status: true };
            }
            return { value: null, status: false };
        };
    }
    async handleDailyAppleReportsSync() {
        try {
            const vendors = await this.AppleAccountRepo.find({});
            const fromDate = moment().subtract(2, 'days').format('YYYY-MM-DD');
            const reportTypes = [AnalyticsReport_schema_1.recordTypeEnum.SALES, AnalyticsReport_schema_1.recordTypeEnum.DOWNLOAD];
            this.LoggerService.logApiCall(`🔁 Starting Apple Report Sync for ${fromDate}`);
            for (const vendor of vendors) {
                for (const reportType of reportTypes) {
                    const alreadySynced = await this.analyticsReportRepo.findOne({
                        AccountID: vendor._id,
                        EventDate: new Date(fromDate),
                        type: reportType
                    });
                    if (!alreadySynced) {
                        try {
                            const syncResult = await this.doSyncAnalyticsReport(reportType, fromDate, vendor);
                            const successMsg = `✅ Synced ${reportType} for Vendor ${vendor.VendorID}: ${syncResult?.count || 0}`;
                            this.LoggerService.logApiCall(successMsg);
                        }
                        catch (err) {
                            const errorMsg = `❌ Sync failed for ${vendor.VendorID} [${reportType}]`;
                            this.LoggerService.logApiCall(errorMsg);
                            console.error(errorMsg, err);
                        }
                    }
                    else {
                        const alreadySyncedMsg = `⚠️ Already synced: ${vendor.VendorID} [${reportType}] on ${fromDate}`;
                        this.LoggerService.logApiCall(alreadySyncedMsg);
                    }
                    ;
                }
                ;
            }
            ;
            this.LoggerService.logApiCall(`✅ Apple Report Sync Completed for ${fromDate}`);
        }
        catch (error) {
            this.LoggerService.logApiCall(`❌ Error in Apple Report Sync: ${error?.message}`);
            console.error(error, 'error......');
        }
        ;
    }
    ;
    async doSyncCountryWisevaluation() {
        console.log(CountryCodeToCurrency_1.CurrencyList?.length);
        return await new Promise(async (resolve, reject) => {
            const requests = CountryCodeToCurrency_1.CurrencyList.map((item, index) => this.convertCurrencyValue(item, "usd"));
            try {
                const result = await Promise.all(requests);
                const existingCurrencies = await this.CurrencyValuationRepo.find();
                const existingMap = new Map(existingCurrencies.map((c) => [c.currency, c]));
                const operations = result.map(async (res) => {
                    if (!res || !res.status || !res.value)
                        return null;
                    const { value, from } = res;
                    const data = {
                        currencyBase: "USD",
                        rates: value,
                        currency: from,
                        syncDate: new Date()
                    };
                    if (existingMap.has(from)) {
                        return this.CurrencyValuationRepo.updateOne({ currency: from }, { $set: data });
                    }
                    else {
                        return this.CurrencyValuationRepo.create({
                            ...data,
                            createdAt: new Date(),
                        });
                    }
                });
                await Promise.all(operations.filter(Boolean));
                resolve({ status: true });
            }
            catch (error) {
                console.error("Error in syncing currency valuation:", error);
                resolve({ status: false, error: JSON.stringify(error) });
            }
            ;
        });
    }
    ;
    async doSyncReportInBatch(from, to, type, applevendor = null) {
        console.log("called");
        const dates = await this.enumerateDaysBetweenDates(from, to);
        const totalDates = dates?.length;
        const recordsBetweenDates = await this.analyticsReportRepo.aggregate([
            {
                $match: {
                    EventDate: {
                        $gte: new Date(from),
                        $lte: new Date(to)
                    },
                    type: type.toUpperCase(),
                },
            },
            {
                $group: {
                    _id: { $dateToString: { format: "%Y-%m-%d", date: "$EventDate" } }
                }
            },
            {
                $project: {
                    _id: 0,
                    date: "$_id"
                }
            },
            {
                $group: {
                    _id: null,
                    dates: { $push: "$date" }
                }
            }
        ]);
        let filteredDates = [];
        if (recordsBetweenDates?.length) {
            const datedRecords = recordsBetweenDates?.length ? recordsBetweenDates[0].dates.sort() : dates;
            dates.forEach((elemnt) => {
                if (!datedRecords?.includes(elemnt)) {
                    filteredDates.push(elemnt);
                }
            });
        }
        else {
            console.log(">>>>>>>>>>>elseeeeeeeee>>>>>>>>>>>");
            filteredDates = dates;
        }
        const requests = filteredDates.map((item, index) => this.doSyncAnalyticsReport(type, item, applevendor));
        console.log("--------TOTAL REQUEST:", requests?.length);
        console.log("RESOLVD------");
        const result = await Promise.all(requests);
        console.log("SYNCED");
        let sum = 0;
        result.map((i) => {
            if (i?.count) {
                sum += i?.count;
            }
        });
        return { status: true, count: sum, result };
    }
    async doHandleSyncReportInBatchAfterCredsSync(vendorID) {
        console.log("IN EVENT for Syncing Batch Vendor Id", vendorID);
        const fromDate = moment().subtract(60, 'd').format('YYYY-MM-DD');
        const toDate = moment().subtract(7, 'd').format('YYYY-MM-DD');
        let syncArr = [];
    }
    async doSyncAnalyticsReport(ReportTYPE, date, applevendor = null) {
        if (ReportTYPE !== AnalyticsReport_schema_1.recordTypeEnum.SALES && ReportTYPE !== AnalyticsReport_schema_1.recordTypeEnum.DOWNLOAD) {
            return { status: false };
        }
        const hitDate = !!date == true ? moment(date).format('YYYY-MM-DD') : moment();
        if (moment(hitDate).format("DD-MM-YYYY") == moment().format("DD-MM-YYYY")) {
            moment(hitDate).add(-3, 'days');
        }
        const baseUrl = await this.doGetReportsUrl(ReportTYPE, hitDate, applevendor);
        console.log("CALLIED----", hitDate);
        return await new Promise((resolve, reject) => {
            this.doFetchReports(baseUrl, applevendor).then((response) => {
                if (response?.status) {
                    console.log("CALLING----", hitDate);
                    this.doHandleDataExtractionFromZIP(response.data, hitDate, ReportTYPE, applevendor).then(async (extractedData) => {
                        if (extractedData?.status == true) {
                            console.log("SAVING----", hitDate, extractedData.data?.length);
                            this.analyticsReportRepo.insertMany(extractedData.data);
                            console.log("RESOLVING------");
                            resolve(extractedData);
                        }
                        else {
                            console.warn("❗ Extraction failed for", hitDate);
                            resolve({ status: false, error: "Extraction Failed" });
                        }
                    });
                }
                else {
                    if (response.code === 401) {
                        console.warn("🔐 Unauthorized: Token expired or invalid for", hitDate);
                        resolve({
                            status: false,
                            code: response.code,
                            message: response.message
                        });
                    }
                    else if (response.code === 404) {
                        console.log("📄 Report not ready for", hitDate);
                        resolve(response);
                    }
                    else {
                        console.error("🚫 Unknown failure for", hitDate);
                        resolve({
                            status: false,
                            code: response.code,
                            message: response.message || "Unknown error"
                        });
                    }
                }
            });
        });
    }
    async doSyncHealth() {
        return {
            status: true,
            code: 200,
            message: 'success'
        };
    }
    async doHandleAccountsSyncing(file) {
        let results = [];
        let jsonRecords = [];
        const result = await new Promise((resolve, reject) => {
            fs.createReadStream(file.path)
                .pipe((0, csv_parse_1.parse)({ delimiter: ',' }))
                .on('data', (data) => results.push(data))
                .on('end', async () => {
                const fromDate = moment().subtract(60, 'd').format('YYYY-MM-DD');
                const toDate = moment().subtract(2, 'd').format('YYYY-MM-DD');
                let requestArr = [];
                let vendorsInStack = [];
                for (let i = 1; i < results.length; i++) {
                    const element = results[i];
                    let tempArr = {};
                    for (let jindex = 0; jindex < element.length; jindex++) {
                        const jElement = element[jindex];
                        tempArr[results[0][jindex].replace(/\s+/g, '')] = jElement;
                    }
                    let record = await this.AppleAccountRepo.findOne({
                        VendorID: tempArr.VenderID
                    });
                    if (record) {
                        await this.AppleAccountRepo.updateMany({}, { $set: { InitialSyncStatus: CurrentStage_enum_1.CurrentStage.INPROGRESS } });
                        await this.AppleAccountRepo.updateOne({ VendorID: record.VendorID }, { $set: { AccountName: tempArr.AccountName } });
                    }
                    if (!vendorsInStack.includes(tempArr.VenderID)) {
                        if (!record) {
                            record = await this.AppleAccountRepo.create({
                                AccountName: tempArr.AccountName,
                                Issuer: tempArr.Issuer,
                                KeyId: tempArr.KeyID,
                                PrivateKey: tempArr.PrivateKey,
                                VendorID: tempArr.VenderID,
                                InitialSyncStatus: CurrentStage_enum_1.CurrentStage.INPROGRESS,
                                SyncDate: new Date()
                            });
                        }
                        this.eventEmitter.emit('sync.Report', fromDate, toDate, record);
                        jsonRecords.push(record);
                        vendorsInStack.push(tempArr.VenderID);
                    }
                }
                console.log(requestArr, "reqqarrry>>>>>>>>>>>>>>>>>>");
                resolve({ data: [], status: true });
                fs.unlinkSync(file.path);
            }).on("error", () => {
                resolve({ data: null, status: false });
            });
        });
        if (result.status == true) {
            return {
                data: await this.AppleAccountRepo.find({}, {
                    VendorID: true,
                    AccountName: true,
                }),
                status: true
            };
        }
        if (result.status == false) {
            return result;
        }
    }
    async doSyncReportBatch(from, to, type, applevendor = null) {
        let syncRecord = await this.analyticsReportRepo.aggregate([
            {
                $match: {
                    EventDate: {
                        $gte: new Date(new Date(from)),
                        $lte: new Date(new Date(to)),
                    },
                    type: type.toUpperCase(),
                },
            },
            {
                $group: {
                    _id: {
                        $dateToString: { format: "%Y-%m-%d", date: "$EventDate" }
                    },
                    doc: { $first: "$$ROOT" }
                }
            },
            {
                $replaceRoot: { newRoot: "$doc" }
            }
        ]);
        return syncRecord;
    }
    async doHandleDataExtractionFromZIP(FILE, hitDate, ReportTYPE, appleVendor = null) {
        const timestamp = moment().unix();
        const filePath = "public/tempReports/" + timestamp + +'_sr.gz';
        fs.writeFileSync(filePath, FILE);
        const decompressedDataString = await zlib?.gunzipSync(FILE)?.toString('utf-8');
        return await new Promise(async (resolve, reject) => {
            (0, csv_parse_1.parse)(decompressedDataString, { delimiter: '\t' }, async (err, data) => {
                if (err) {
                    console.error(err);
                    resolve({ status: false, error: err, data: [], count: 0 });
                }
                let array = [];
                let currencyRecord = await this.CurrencyValuationRepo.find();
                for (let index = 1; index < data.length; index++) {
                    const element = data[index];
                    let tempArr = {};
                    for (let jindex = 0; jindex < element.length; jindex++) {
                        const jElement = element[jindex];
                        tempArr[data[0][jindex].replace(/\s+/g, '')] = jElement;
                    }
                    if (ReportTYPE == AnalyticsReport_schema_1.recordTypeEnum.SALES) {
                        if (tempArr?.CustomerPrice && tempArr?.ProceedsCurrency.toLowerCase() == "usd" && tempArr?.DeveloperProceeds != "0.00") {
                            let result = await this.analyticsService.doStoreICon(tempArr?.AppAppleID);
                            if (result && result?.data) {
                                let obj = { ...tempArr, Units: Number(tempArr?.Units), EventDate: hitDate, CustomerProceedsInUsd: Number(tempArr?.DeveloperProceeds), type: ReportTYPE, iconurl: result.data };
                                if (!!appleVendor == true) {
                                    obj.AccountID = appleVendor._id;
                                }
                                array.push(obj);
                            }
                            else {
                                let obj = { ...tempArr, Units: Number(tempArr?.Units), EventDate: hitDate, CustomerProceedsInUsd: Number(tempArr?.DeveloperProceeds), type: ReportTYPE };
                                if (!!appleVendor == true) {
                                    obj.AccountID = appleVendor._id;
                                }
                                array.push(obj);
                            }
                        }
                        if (tempArr?.CustomerPrice && tempArr?.DeveloperProceeds != "0.00" && tempArr?.DeveloperProceeds != "0.0" && tempArr?.ProceedsCurrency !== "USD") {
                            let checkCurrency = currencyRecord.find((i) => i.currency === tempArr?.ProceedsCurrency);
                            if (checkCurrency) {
                                let calculateCurrency = Number(tempArr?.DeveloperProceeds) * Number(checkCurrency.rates);
                                let result = await this.analyticsService.doStoreICon(tempArr?.AppAppleID);
                                if (result && result?.data) {
                                    let obj = { ...tempArr, Units: Number(tempArr?.Units), EventDate: hitDate, CustomerProceedsInUsd: calculateCurrency.toFixed(2), type: ReportTYPE, iconurl: result.data };
                                    if (!!appleVendor == true) {
                                        obj.AccountID = appleVendor._id;
                                    }
                                    array.push(obj);
                                }
                                else {
                                    let obj = { ...tempArr, Units: Number(tempArr?.Units), EventDate: hitDate, CustomerProceedsInUsd: calculateCurrency.toFixed(2), type: ReportTYPE };
                                    if (!!appleVendor == true) {
                                        obj.AccountID = appleVendor._id;
                                    }
                                    array.push(obj);
                                }
                            }
                        }
                    }
                    else if (ReportTYPE == AnalyticsReport_schema_1.recordTypeEnum.DOWNLOAD) {
                        let result = await this.analyticsService.doStoreICon(tempArr?.AppleIdentifier);
                        if (result && result?.data) {
                            array.push({ ...tempArr, Units: Number(tempArr?.Units), EventDate: hitDate, type: ReportTYPE, AccountID: appleVendor._id, iconurl: result.data });
                        }
                        else {
                            array.push({ ...tempArr, Units: Number(tempArr?.Units), EventDate: hitDate, type: ReportTYPE, AccountID: appleVendor._id });
                        }
                    }
                }
                resolve({ status: true, count: array?.length, data: array });
            });
        });
    }
    async doGenerateTokenAPI({ iss, privateKey, keyId }) {
        try {
            return await (0, rxjs_1.lastValueFrom)(this.httpService.post('http://localhost:3000/generate-token', {
                iss: iss,
                keyId: keyId,
                privateKey: privateKey
            }).pipe((0, rxjs_1.map)(response => response.data.access_token)));
        }
        catch (error) {
            return {
                status: false,
                data: [],
                message: error.message,
                code: error.status,
                error: error?.response?.data
            };
        }
    }
    async doFetchReports(baseUrl, accountRecord = null) {
        const access_token = await this.doGenerateTokenAPI({
            iss: accountRecord.Issuer, privateKey: accountRecord?.PrivateKey, keyId: accountRecord?.KeyId
        });
        try {
            return await (0, rxjs_1.lastValueFrom)(this.httpService.get(baseUrl, {
                headers: {
                    'Accept': 'application/a-gzip',
                    'Authorization': `Bearer ${access_token}`
                },
                responseType: 'arraybuffer',
            }).pipe((0, rxjs_1.map)(response => {
                if (response.data) {
                    return {
                        status: true,
                        data: response.data,
                        code: response.status,
                        message: "Data Fetched!"
                    };
                }
            })));
        }
        catch (error) {
            console.log("errror>>>>>>>>>>", baseUrl);
            console.error("Error Message:", error.message);
            if (error.response) {
                console.error("Status:", error.response.status);
                console.error("Data:", error.response.data?.toString?.() || error.response.data);
            }
            return {
                status: false,
                data: [],
                message: error.message,
                code: error.response?.status,
                error: error?.response?.data
            };
        }
    }
    doGetReportsUrl(type = null, gDate, appleVendor) {
        if (!type && !gDate) {
            return null;
        }
        const date = moment(gDate, this.APPLEDATEFORMAT);
        if (type == AnalyticsReport_schema_1.recordTypeEnum.DOWNLOAD) {
            return process.env.appleBaseUrl + process.env.salesReport + `?filter[frequency]=DAILY&filter[reportDate]=${date.format(this.APPLEDATEFORMAT)}&filter[reportSubType]=SUMMARY&filter[reportType]=SALES&filter[vendorNumber]=${appleVendor.VendorID}&filter[version]=1_0`;
        }
        if (type == AnalyticsReport_schema_1.recordTypeEnum.SALES) {
            return process.env.appleBaseUrl + process.env.salesReport + `?filter[frequency]=DAILY&filter[reportDate]=${date.format(this.APPLEDATEFORMAT)}&filter[reportSubType]=DETAILED&filter[reportType]=SUBSCRIBER&filter[vendorNumber]=${appleVendor.VendorID}&filter[version]=1_3`;
        }
    }
    async convertCurrencyValue(from, to) {
        const fromUpper = from.toUpperCase();
        const toUpper = to.toUpperCase();
        const today = new Date().toISOString().split('T')[0];
        const primaryUrl = `https://open.er-api.com/v6/latest/${fromUpper}`;
        const getRates = async (url, label) => {
            try {
                const response = await (0, rxjs_1.lastValueFrom)(this.httpService.get(url).pipe((0, rxjs_1.timeout)(20000), (0, rxjs_1.retryWhen)(errors => errors.pipe((0, rxjs_1.delay)(2000), (0, rxjs_1.take)(2))), (0, rxjs_1.map)(res => res.data), (0, rxjs_1.catchError)(err => {
                    console.error(`❌ ${label} failed:`, err.message || err.code);
                    return (0, rxjs_1.of)(null);
                })));
                return response;
            }
            catch (e) {
                console.error(`🚨 Error during fetch from ${label}:`, e.message || e.code);
                return null;
            }
        };
        const response = await getRates(primaryUrl, "Open ER API");
        if (!response || !response.rates) {
            return { status: false, message: 'API response is invalid or rates missing.' };
        }
        const rate = response.rates[toUpper];
        if (!rate) {
            return { status: false, message: `Conversion rate from ${fromUpper} to ${toUpper} not found.` };
        }
        return {
            status: true,
            from: fromUpper,
            to: toUpper,
            rate,
            value: rate,
            date: response.time_last_update_utc || today,
        };
    }
    enumerateDaysBetweenDates(startDate, endDate) {
        let dates = [];
        const currDate = moment(startDate, "YYYY-MM-DD");
        const lastDate = moment(endDate, "YYYY-MM-DD");
        while (lastDate.diff(currDate) >= 0) {
            dates.push(moment(currDate).format("YYYY-MM-DD"));
            currDate.add(1, 'days');
        }
        return dates;
    }
    ;
};
exports.CronSyncService = CronSyncService;
__decorate([
    (0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_DAY_AT_7PM),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], CronSyncService.prototype, "handleDailyAppleReportsSync", null);
__decorate([
    (0, schedule_1.Cron)(schedule_1.CronExpression.EVERY_5_SECONDS),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], CronSyncService.prototype, "doSyncCountryWisevaluation", null);
exports.CronSyncService = CronSyncService = CronSyncService_1 = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, mongoose_1.InjectModel)("CurrencyValuation")),
    __param(1, (0, mongoose_1.InjectModel)("AnalyticsReport")),
    __param(2, (0, mongoose_1.InjectModel)("AppleAccount")),
    __metadata("design:paramtypes", [mongoose_2.default.Model, mongoose_2.default.Model, mongoose_2.default.Model, AnalyticsReport_service_1.AnalyticsReportService,
        event_emitter_1.EventEmitter2,
        axios_1.HttpService,
        jwt_1.JwtService,
        logger_service_1.LoggerService])
], CronSyncService);
//# sourceMappingURL=CronSyncService.service.js.map