// tslint:disable:no-console
// tslint:disable:no-console
import { CognitoIdentityProvider } from '@aws-sdk/client-cognito-identity-provider';
// tslint:disable-next-line:no-submodule-imports
import type { UserPoolClientType } from '@aws-sdk/client-cognito-identity-provider/dist-types/models/models_0';
import { fromEnv } from '@aws-sdk/credential-providers';
import * as fs from 'fs';
// tslint:disable-next-line:no-implicit-dependencies
import * as json2csv from 'json2csv';
import * as mongoose from 'mongoose';

import { chevre } from '../../../../lib/index';

// const project = { id: String(process.env.PROJECT_ID) };
// const excludedProject = { id: String(process.env.EXCLUDED_PROJECT_ID) };
const userPoolId = `${process.env.COGNITO_USER_POOL_ID}`;
const oldProvider = `https://cognito-idp.ap-northeast-1.amazonaws.com/${userPoolId}`;
const NEW_ISS = process.env.NEW_ISS;

// tslint:disable-next-line:max-func-body-length
async function main() {
    if (typeof NEW_ISS !== 'string') {
        throw new Error('process.env.NEW_ISS undefined');
    }

    const awsCredentials = fromEnv();
    const cognitoIdentityServiceProvider = new CognitoIdentityProvider({
        apiVersion: 'latest',
        region: 'ap-northeast-1',
        credentials: awsCredentials
    });

    await mongoose.connect(<string>process.env.MONGOLAB_URI, { autoIndex: false });

    const identityRepo = await chevre.repository.Identity.createInstance(mongoose.connection);
    const identityProviderRepo = await chevre.repository.IdentityProvider.createInstance(mongoose.connection);
    const memberRepo = await chevre.repository.Member.createInstance(mongoose.connection);

    const cursor = identityRepo.getCursor(
        {
            'issuedBy.identifier': { $eq: oldProvider }
            // _id: { $eq: '67de46777ec0510590b68922' }
        },
        {
            _id: 1,
            about: 1,
            project: 1,
            typeOf: 1,
            issuedBy: 1
        }
    );
    console.log('docs found');

    let i = 0;
    let updateCount = 0;
    const creatingClients: {
        projectId: string;
        clientId: string;
        clientSecret: string;
        name: string;
        memberName: string;
    }[] = [];
    // tslint:disable-next-line:max-func-body-length
    await cursor.eachAsync(async (doc) => {
        i += 1;
        const identity: Pick<
            chevre.factory.creativeWork.certification.webApplication.ICertification,
            'about' | 'project' | 'typeOf' | 'id' | 'issuedBy'
        > = doc.toObject();

        const iamMember = (await memberRepo.projectFieldsByProjectId(
            { id: identity.project.id },
            {
                limit: 1,
                member: {
                    id: { $eq: identity.about.id },
                    typeOf: { $eq: chevre.factory.creativeWorkType.WebApplication }
                }
            },
            ['member']
        )).shift();
        if (iamMember === undefined) {
            throw new Error(`iamMember not found ${identity.project.id} ${identity.about.id}`);
        }

        const client = await new Promise<UserPoolClientType>(async (resolve, reject) => {
            cognitoIdentityServiceProvider.describeUserPoolClient(
                {
                    ClientId: identity.about.id,
                    UserPoolId: userPoolId
                },
                {},
                (err, data) => {
                    if (err instanceof Error) {
                        reject(err);
                    } else {
                        if (data?.UserPoolClient === undefined) {
                            reject(new chevre.factory.errors.NotFound('UserPoolClient'));
                        } else {
                            resolve(data.UserPoolClient);
                        }
                    }
                }
            );
        });

        if (typeof client.ClientId !== 'string'
            || typeof client.ClientSecret !== 'string'
            || typeof client.ClientName !== 'string') {
            throw new Error('invalid client');
        }

        creatingClients.push({
            projectId: identity.project.id,
            clientId: client.ClientId,
            clientSecret: client.ClientSecret,
            name: client.ClientName,
            memberName: String(iamMember.member.name)
        });

        const issuedBy = identity.issuedBy;
        const numIssuedBy = (Array.isArray(identity.issuedBy)) ? identity.issuedBy.length : 1;

        const alreadyMigrated = (Array.isArray(issuedBy))
            // tslint:disable-next-line:no-magic-numbers
            && issuedBy.length === 2
            && issuedBy.some(({ identifier }) => identifier === NEW_ISS);

        if (alreadyMigrated) {
            console.log('already migrated.', identity.project.id, identity.about.id, identity.id, numIssuedBy, i);
        } else {
            if (typeof identity.id !== 'string') {
                throw new Error(`id undefined ${identity.id}`);
            }

            // プロバイダー追加
            const existingProvider = (await identityProviderRepo.projectFields(
                {
                    limit: 1,
                    project: { id: { $eq: identity.project.id } },
                    identifier: { $eq: NEW_ISS }
                },
                ['identifier']
            )).shift();
            if (existingProvider === undefined) {
                await identityProviderRepo.save({
                    attributes: {
                        identifier: NEW_ISS,
                        project: { id: identity.project.id, typeOf: chevre.factory.organizationType.Project },
                        typeOf: chevre.factory.organizationType.Organization
                    }
                });
                console.log('provider saved.', identity.project.id, identity.about.id, identity.id, numIssuedBy, i);
            }

            // identity更新
            const newIssuedBy = (Array.isArray(issuedBy)) ? issuedBy : [issuedBy];
            if (newIssuedBy.length !== 1) {
                throw new Error('newIssuedBy.length must be 1');
            }
            newIssuedBy.push({
                identifier: NEW_ISS,
                typeOf: chevre.factory.organizationType.Organization
            });
            console.log(newIssuedBy);
            await identityRepo.updateIssuedBy2array({
                id: identity.id,
                issuedBy: newIssuedBy
            });
            updateCount += 1;
            console.log(
                'updated.', identity.project.id, identity.about.id, identity.id, numIssuedBy, i);
        }
    });

    console.log(creatingClients.map(({ clientId }) => clientId));
    // tslint:disable-next-line:non-literal-fs-path no-null-keyword
    // fs.writeFileSync(`${__dirname}/../../../creatingClients.json`, JSON.stringify(creatingClients, null, '    '));
    // tslint:disable-next-line:non-literal-fs-path no-null-keyword
    const json = JSON.stringify(creatingClients, null, '');
    // tslint:disable-next-line:non-literal-fs-path
    fs.writeFileSync(`${__dirname}/../../../creatingClients.json`, json);
    console.log(i, 'docs checked');
    console.log(updateCount, 'docs updated');

    const parser = new json2csv.Parser({
        fields: ['projectId', 'clientId', 'clientSecret', 'name', 'memberName']
    });
    const csv = parser.parse(creatingClients.sort(
        (a, b) => {
            return (a.projectId > b.projectId) ? 1 : -1;
        }
    ));
    // tslint:disable-next-line:non-literal-fs-path
    fs.writeFileSync(`${__dirname}/../../../creatingClients.csv`, csv);
}

main()
    .then()
    .catch(console.error);
