import TestApp from "../src/TestApp";
import useApp from "./_app";
import Controller from "../src/Controller";
import supertest from "supertest";
import CsrfProtectionComponent from "../src/components/CsrfProtectionComponent";
import UserPasswordComponent from "../src/auth/password/UserPasswordComponent";
import {popEmail} from "./_mail_server";
import AuthComponent from "../src/auth/AuthComponent";
import Migration, {MigrationType} from "../src/db/Migration";
import AddNameToUsersMigration from "../src/auth/migrations/AddNameToUsersMigration";
import {followMagicLinkFromMail, testLogout} from "./_authentication_common";
import UserEmail from "../src/auth/models/UserEmail";

let app: TestApp;
useApp(async (addr, port) => {
    return app = new class extends TestApp {
        protected async init(): Promise<void> {
            this.use(new class extends Controller {
                public routes(): void {
                    this.get('/', (req, res) => {
                        res.render('home');
                    }, 'home');
                    this.get('/csrf', (req, res) => {
                        res.send(CsrfProtectionComponent.getCsrfToken(req.getSession()));
                    }, 'csrf');
                    this.get('/is-auth', async (req, res) => {
                        const proofs = await this.getApp().as(AuthComponent).getAuthGuard().getProofs(req);
                        if (proofs.length > 0) res.sendStatus(200);
                        else res.sendStatus(401);
                    }, 'is-auth');
                }
            }());

            await super.init();
        }

        protected getMigrations(): MigrationType<Migration>[] {
            return super.getMigrations().filter(m => m !== AddNameToUsersMigration);
        }
    }(addr, port, true);
});

let agent: supertest.SuperTest<supertest.Test>;

beforeAll(() => {
    agent = supertest(app.getExpressApp());
});

describe('Register with username and password (password)', () => {
    test('Must be disabled', async () => {
        const res = await agent.get('/csrf').expect(200);
        const cookies = res.get('Set-Cookie');
        const csrf = res.text;

        // Register user
        await agent.post('/auth/register')
            .set('Cookie', cookies)
            .send({
                csrf: csrf,
                auth_method: 'password',
                identifier: 'entrapta',
                password: 'darla_is_cute',
                password_confirmation: 'darla_is_cute',
                terms: 'on',
            })
            .expect(500);
    });
});

describe('Register with email (magic_link)', () => {
    test('General case', async () => {
        const res = await agent.get('/csrf').expect(200);
        const cookies = res.get('Set-Cookie');
        const csrf = res.text;

        await agent.post('/auth/register')
            .set('Cookie', cookies)
            .send({
                csrf: csrf,
                auth_method: 'magic_link',
                identifier: 'glimmer@example.org',
            })
            .expect(302)
            .expect('Location', '/magic/lobby?redirect_uri=');

        await followMagicLinkFromMail(agent, cookies);

        await testLogout(agent, cookies, csrf);

        // Verify saved user
        const email = await UserEmail.select()
            .with('user')
            .where('email', 'glimmer@example.org')
            .first();
        const user = email?.user.getOrFail();

        expect(user).toBeDefined();

        expect(email).toBeDefined();
        expect(email?.email).toStrictEqual('glimmer@example.org');

        await expect(user?.as(UserPasswordComponent).verifyPassword('')).resolves.toStrictEqual(false);
    });

    test('Cannot register taken email', async () => {
        const res = await agent.get('/csrf').expect(200);
        const cookies = res.get('Set-Cookie');
        const csrf = res.text;

        await agent.post('/auth/register')
            .set('Cookie', cookies)
            .send({
                csrf: csrf,
                auth_method: 'magic_link',
                identifier: 'bow@example.org',
                name: 'bow',
            })
            .expect(302)
            .expect('Location', '/magic/lobby?redirect_uri=');

        await followMagicLinkFromMail(agent, cookies);

        // Verify saved user
        const userEmail = await UserEmail.select()
            .with('user')
            .where('email', 'bow@example.org')
            .first();
        const user = userEmail?.user.getOrFail();

        expect(user).toBeDefined();

        // Attempt register with another mail but same username
        const res2 = await agent.get('/csrf').expect(200);

        await agent.post('/auth/register')
            .set('Cookie', res2.get('Set-Cookie'))
            .send({
                csrf: res2.text,
                auth_method: 'magic_link',
                identifier: 'bow@example.org',
                name: 'bow2',
            })
            .expect(400);

        expect(await popEmail()).toBeNull();
    });
});