import { Account, Agency, BinomUser, BusinessDirection, PrismaClient, RequestStatus, SystemSettings, Team, Timezone, ProviderAccount, TopUpType, TrafficSource, User, Vertical, WorkerEntity, TonicCampaignStatus, OfferStatus } from "@prisma/client";
import bcrypt from 'bcryptjs';
import { getRandomNumber, getRandomValue } from "../utils/math";
import moment from "moment-timezone";
import { TcpNetConnectOpts } from "net";

async function main() {
    const client = new PrismaClient();

    // await fixTonicCampaigns(client);
    // basic entities
    // await addSettings(settings, client);
    // await addTimezones(timezones, client);
    // await addAgencies(agencies, client);
    // await addBusinessDirections(businessDirections, client);

    // // single/one to one entity
    // await addTeams(teams, client);
    // await addBinomUsers(binomUsers, client);
    // await addUsers(users, client);
    // await addTonicAccounts(tonicAccounts, client);
    // await addAccounts(accounts, client);
    // await addSpend(client);
    // await addAccountActivities(client);
    // await addAccountRequests(client);
    // await addTopUpRequest(client);

    // //many to many entity
    // await addAccountsToUsers(client);

    await client.$disconnect();
}

async function createFinanceProfiles(client: PrismaClient) {
    const users = await client.user.findMany({
        where: {
            financeProfile: {
                is: null
            }
        }
    })

    for(let user of users) {
        await client.financeProfile.create({
            data: {
                userFinanceProfile: {
                    create: {
                        userId: user.id,
                        userFinanceProfileType: 'FRONT_OFFICE'
                    }
                },
                financeProfileEntityType: 'USER',
                payoutCurrency: 'USD'
            }
        })
    }

    const teams = await client.team.findMany({
        where: {
            financeProfile: {
                is: null
            }
        }
    })

    for(let team of teams) {
        await client.financeProfile.create({
            data: {
                teamFinanceProfile: {
                    create: {
                        teamId: team.id
                    }
                },
                financeProfileEntityType: 'TEAM',
                payoutCurrency: 'USD'
            }
        })
    }
}
/*
async function fixTonicCampaigns(client: PrismaClient) {
    const offerSelect = {
        id: true
    };

    const dateRevenue = await client.dateRevenue.findMany({
        distinct: ['providerEntityId'],
        where: {
            amount: {
                gt: 0
            },
            providerEntity: {
                provider: 'TONIC'
            },
            offer: {
                is: null
            }
        },
        select: {
            providerEntity: {
                select: {
                    tonicCampaign: {
                        select: {
                            id: true
                        }
                    }
                }
            }
        }
    })

    const dateRevenueIdsTonicCampaignIds = dateRevenue.map( dr => dr.providerEntity?.tonicCampaign?.id).filter( id => Boolean(id)) as number[];

    console.log(`Found ${dateRevenueIdsTonicCampaignIds.length} unique tonic campaign ids from date revenue`);

    const hashedDateRevenueIdsTonicCampaignIds = dateRevenueIdsTonicCampaignIds.reduce((table, id) => {
        table[id] = id;
        return table;
    }, {} as Record<string,number>)

    let tonicCampaigns = await client.tonicCampaign.findMany({
        orderBy: {
            tonicCreateDate: {
                sort: 'desc',
                nulls: 'last'
            }
        },
        select: {
            offer: {
                select: offerSelect
            },
            providerEntity: {
                select: {
                    providerAccount: {
                        select: {
                            trafficSource: true,
                            id: true
                        }
                    },
                    id: true
                },
            },
            providerAccountId: true,
            name: true,
            status: true,
            id: true
        },
        where: {
            offer: {
                is: null
            }
        },
    });

    console.log(`Found ${tonicCampaigns.length} tonic campaigns in db.`);

    tonicCampaigns = tonicCampaigns.filter( tc => {
        return hashedDateRevenueIdsTonicCampaignIds[tc.id] || (tc.status && (['active', 'incomplete', 'pending', 'stopped'] as TonicCampaignStatus[]).includes(tc.status))
    })

    console.log(`Filtered tonic campaigns. new length is ${tonicCampaigns.length} `);
    
    const owner = await client.user.findFirst({
        where: {
            role: 'OWNER'
        },
        select: {
            id: true
        }
    })
    
    if(!owner) throw new Error(`No owner exists, unable to assign offers`);
    
    for(let [index, tonicCampaign] of Object.entries(tonicCampaigns)) {
        try {
            let offer = tonicCampaign.offer;
            if(!offer) {
                let providerAccountId = tonicCampaign.providerEntity.providerAccount?.id;
                if(!providerAccountId) providerAccountId = tonicCampaign.providerAccountId ?? undefined;
                if(!providerAccountId) throw new Error(`Tonic campaign ${tonicCampaign.id} does not have connected provider account through provider entity`);
    
                let offerStatus:  OfferStatus;
                let errorMessage: string | undefined = undefined;
                switch (tonicCampaign.status) {
                    case 'active':
                        offerStatus = 'ACTIVE';
                        break;
                    case 'pending':
                        offerStatus = 'PENDING'
                    default:
                        offerStatus = 'ERROR';
                        errorMessage = `Tonic campaign status is ${tonicCampaign.status}`;
                        break;
                }
    
                offer = await client.offer.create({
                    data: {
                        provider: 'TONIC',
                        tf: tonicCampaign.providerEntity.providerAccount?.trafficSource ?? 'FACEBOOK',
                        tracker: 'BINOM_V2',
                        type: 'RSOC',
                        userId: owner.id,
                        name: tonicCampaign.name ?? '',
                        status: offerStatus,
                        providerAccountId: providerAccountId,
                        errorDescription: errorMessage,
                        tonicCampaign: {
                            connect: {
                                id: tonicCampaign.id
                            }
                        },
                    },
                    select: {
                        ...offerSelect
                    }
                })

                console.log(`${+index + 1}/${tonicCampaigns.length}Offer ${offer.id} added for tonic campaign ${tonicCampaign.id}.`);
                
            }

            const updatedDateRevenue = await client.dateRevenue.updateManyAndReturn({
                where: {
                    providerEntityId: tonicCampaign.providerEntity.id
                },
                data: {
                    offerId: offer.id
                },
                select: {
                    id: true
                }
            })

            console.log(`Updated ${updatedDateRevenue.length} date revenues.`);
        } catch (e: any) {
            console.log(e.message);
        }
    }
}
*/
// async function addTopUpRequest(client: PrismaClient) {
//     const allAccounts = await client.account.findMany({include: {
//         topUps: true
//     }});
//     const allUsers = await client.user.findMany();
//     const allPlatforms = Object.values(TrafficSource);
//     const requestStatuses = Object.values(RequestStatus);
//     const allTopUpTypes = Object.values(TopUpType);
// }

//     for(let user of users) {
//         await userFinanceProfile({
//             userId: user.id,
//             financeProfile: user.userFinanceProfile && {
//                 id: user.userFinanceProfile.id
//             },
//             financeProfiles: user.financeProfiles
//         })
//     }
// }

// async function userFinanceProfile({
//     userId,
//     financeProfile,
//     financeProfiles
// }: {
//     userId: number,
//     financeProfiles: {
//         id: number
//     }[],
//     financeProfile: {
//         id: number
//     } | null
// }) {
//     if(!financeProfile) {
//         if(financeProfiles.length > 0) { // if exists
//             await client.user.update({
//                 data: {
//                     userFinanceProfileId: financeProfiles[0].id
//                 },
//                 where: {
//                     id: userId
//                 }
//             })
//         } else {
//             await client.user.update({
//                 data: {
//                     userFinanceProfile: {
//                         create: {
//                             name: '',
//                             userId: userId,
//                             monthlyBaseSalary: 0,
//                             financeProfileType: 'FRONT_OFFICE'
//                         }
//                     }
//                 },
//                 where: {
//                     id: userId
//                 }
//             })
//         }
//     }
// }

// async function addTopUpRequest(client: PrismaClient) {
//     const allAccounts = await client.account.findMany({include: {
//         topUps: true
//     }});
//     const allUsers = await client.user.findMany();
//     const allPlatforms = Object.values(TrafficSource);
//     const requestStatuses = Object.values(RequestStatus);
//     const allTopUpTypes = Object.values(TopUpType);
// }

// async function addAccountRequests(client: PrismaClient) {
// }

// async function addAccountActivities(client: PrismaClient) {
//     const allAccounts = await client.account.findMany();

//     for(const account of allAccounts) {
//         await client.accountActivity.create({
//             data: {
//                 type: 'BANNED',
//                 eventTime: moment().subtract(getRandomNumber(0, 10), 'days').toDate(),
//                 accountId: account.id
//             }
//         })

//         await client.accountActivity.create({
//             data: {
//                 type: 'UNBANNED',
//                 eventTime: moment().subtract(getRandomNumber(0, 10), 'days').toDate(),
//                 accountId: account.id
//             }
//         })

//         const topUpOldValue = getRandomNumber(100, 1000);
//         const topValue = getRandomNumber(100, 1000);

//         await client.accountActivity.create({
//             data: {
//                 type: 'TOPUP',
//                 eventTime: moment().subtract(getRandomNumber(0, 10), 'days').toDate(),
//                 accountId: account.id,
//                 actor: 'Random actor',
//                 value: topValue,
//                 newValue: topUpOldValue + topValue,
//                 oldValue: topUpOldValue,
//             }
//         })

//         const withdrawalOldValue = getRandomNumber(1000, 2000);
//         const withdrawalValue = getRandomNumber(100, 1000);

//         await client.accountActivity.create({
//             data: {
//                 type: 'WITHDRAWAL',
//                 eventTime: moment().subtract(getRandomNumber(0, 10), 'days').toDate(),
//                 accountId: account.id,
//                 actor: 'Random actor',
//                 value: withdrawalValue,
//                 newValue: withdrawalOldValue - withdrawalValue,
//                 oldValue: withdrawalOldValue,
//             }
//         })
//     }
// }

// async function addSpend(client: PrismaClient) {
//     const allAccounts = await client.account.findMany();
//     await client.adStat.deleteMany();

//     for(let i = 0; i < 1000; i++) {
//         const randomAccount = getRandomValue(allAccounts);
//         await client.adStat.create({
//             data: {
//                 accountId: randomAccount.accountId,
//                 adgroupId: randomAccount.accountId,
//                 adId: randomAccount.accountId + i.toString(),
//                 campaignId: randomAccount.accountId,
//                 clicks: getRandomNumber(100, 1000),
//                 conversions: getRandomNumber(1, 100),
//                 date: moment().subtract(getRandomNumber(0, 10), 'days').toDate(),
//                 impressions: getRandomNumber(1000, 10000),
//                 spend: getRandomNumber(0, 100),
//                 accountIdRef: randomAccount.id
//             }
//         })
//     }
// }

// async function addBinomUsers(data: BinomUser[], client: PrismaClient) {
//     const existedBU = await client.binomUser.findMany();

//     for(const bu of data) {
//         const existedBu = existedBU.find(existedBd => existedBd.name === bu.name);

//         if(existedBu) continue;

//         await client.binomUser.create({
//             data: bu
//         })
//     }
// }

// async function addTeams(data: Team[], client: PrismaClient) {
//     const businessDirections = await client.businessDirection.findMany();

//     for(const team of data) {
//         const existingTeams = await client.team.findMany();
//         const existingTeam = existingTeams.find(existedTeam => existedTeam.name === team.name);

//         if(existingTeam) continue;

//         const admin = await client.user.findFirst({
//             where: {
//                 role: 'OWNER'
//             }
//         })

//         await client.team.create({
//             data: {
//                 ...team,
//                 managerId: admin?.id,
//                 businessDirectionId: getRandomValue(businessDirections)?.id,
//                 parentTeamId: getRandomValue(existingTeams)?.id
//             }
//         })
//     }
// }

// async function addBusinessDirections(data: BusinessDirection[], client: PrismaClient) {
//     const existedBDs = await client.businessDirection.findMany();

//     for(const bd of data) {
//         const existedBd = existedBDs.find(existedBd => existedBd.name === bd.name);

//         if(existedBd) continue;

//         await client.businessDirection.create({
//             data: bd
//         })
//     }
// }

// async function addAccountsToUsers(client: PrismaClient) {
//     const exitingAccountsToUsers = await client.accountUser.findMany();

//     const existingAccounts = await client.account.findMany();
//     const existingUsers = await client.user.findMany();

//     for(let existingAccount of existingAccounts) {
//         for(let existingUser of existingUsers) {
//             const connectionExist = exitingAccountsToUsers.find( connection => {
//                 return connection.accountId === existingAccount.id && connection.userId === existingUser.id
//             });

//             if(connectionExist) continue;

//             await client.accountUser.create({
//                 data: {
//                     accountId: existingAccount.id,
//                     userId: existingUser.id
//                 }
//             })
//         }
//     }
// }

// async function addAccounts(accounts: Account[], client: PrismaClient) {
//     const existedAccounts = await client.account.findMany();
//     const allAgencies = await client.agency.findMany();

//     for(let account of accounts) {
//         const timezoneExist = existedAccounts.find( a => a.accountId === account.accountId);

//         if(timezoneExist) continue;
        
//         await client.account.create({
//             data: {
//                 ...account,
//                 agencyId: getRandomValue(allAgencies).id,

//             }
//         }) 
//     }
// }

// async function addTimezones(data: Timezone[], client: PrismaClient) {
//     const dbTimezones = await client.timezone.findMany();

//     for(let timezone of data) {
//         const timezoneExist = dbTimezones.find( tz => tz.name === timezone.name);

//         if(timezoneExist) continue;
        
//         await client.timezone.create({
//             data: timezone,
//         }) 
//     }
// }

// async function addAgencies(data: Agency[], client: PrismaClient) {
//     const agencies = await client.agency.findMany();

//     for(let agencyToAdd of data) {
//         const agencyExist = agencies.find( agency => agency.name === agencyToAdd.name);

//         if(agencyExist) continue;
        
//         await client.agency.create({
//             data: agencyToAdd
//         }) 
//     }
// }

// async function addSettings(data: SystemSettings, client: PrismaClient) {
//     const settings = await client.systemSettings.findMany();

//     if (settings.length > 0) return;

//     await client.systemSettings.create({
//         data
//     })
// }

// async function addUsers(data: User[], client: PrismaClient) {
//     const users = await client.user.findMany();
//     const existingTeams = await client.team.findMany();

//     for(let user of data as Required<User>[]) { // Users
//         if(isUserExistInArray(user, users)) continue;

//         const password = await bcrypt.hash(user.password, 10);
//         const availableBinomUsers = await client.binomUser.findMany({
//             where: {
//                 user: null
//             },
//             include: {
//                 user: true
//             }
//         })

//         user.password = password;
//         await client.user.create({
//             data: {
//                 ...user,
//                 teamId: getRandomValue(existingTeams).id,
//                 binomUserId: getRandomValue(availableBinomUsers)?.id
//             }
//         });
//     }
// }

// async function addTonicAccounts(data: ProviderAccount[], client: PrismaClient) {
//     const tonicAccounts = await client.providerAccount.findMany();

//     for(let account of data) {
//         const tonicAccountExist = tonicAccounts.find(tonicAccount => tonicAccount.consumerKey === account.consumerKey);

//         if(!tonicAccountExist) {
//             await client.providerAccount.create({
//                 data: account
//             })
//         }
//     }
// }

// // async function addWorkers(data: WorkerEntity[], client: PrismaClient) {
// //     const workers = await client.workerEntity.findMany();

// //     for(let worker of data) {
// //         if(isWorkerExistInArray(worker, workers)) continue; // if exist - do not add

// //         await client.workerEntity.create({
// //             data: worker
// //         })
// //     }
// // }

// function isWorkerExistInArray(worker: WorkerEntity, array: WorkerEntity[]): boolean {
//     const existedWorker = array.find(dbWorker => (dbWorker.fileName === worker.fileName) && (dbWorker.type === worker.type));

//     return existedWorker ? true : false;
// }

// function isUserExistInArray(user: User, array: User[]): boolean {
//     const existedUser = array.find(dbUser => dbUser.email === user.email);

//     return existedUser ? true : false;
// }

main();