const request = require('supertest');
const app = require('./app');
const fs = require('fs').promises;

const maxCustomerFieldLengths = {
    firstname: 20,
    lastname: 20,
    email: 25,
    phone: 20,
    address: 50,
    city: 20,
    state: 50,
    zip: 10,
    username: 20,
    password: 20
};

describe('Authentication & Authorization', () => {
    const adminUrls = [
        "/customer",
        "/customer/1/show",
        "/customer/addrecord",
        "/customer/1/edit",
        "/product",
        "/product/addrecord",
        "/product/1/edit",
        "/saleorder",
        "/saleorder/1/show",
        "/saleorder/addrecord",
        "/saleorder/1/edit",
        "/orderdetail",
        "/orderdetail/1/show",
        "/orderdetail/addrecord",
        "/orderdetail/1/edit",
        "/promotion",
        "/promotion/addrecord",
        "/promotion/1/edit",
        "/report",
        "/report/customer",
        "/report/product",
        "/report/sale"
    ];

    adminUrls.forEach(function(url) {
        test('Test admin-only middleware correctly rejects non-admin users and redirects to homepage: ' + url, async () => {
            const res = await request(app).get(url);
            expect(res.statusCode).toBe(302);
            expect(res.headers.location).toBe('/');
        });
    });
});

function generateRandomCustomer(invaldField) {
    const symbols = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890!@#$%^&*()";
    let customer = {};
    for (const field in maxCustomerFieldLengths) {
        let length = field === invaldField ? maxCustomerFieldLengths[field] + 1 : maxCustomerFieldLengths[field];
        let value = "";
        for (let i = 0; i < length; i++) {
            value += symbols.charAt(Math.floor(Math.random() * symbols.length));
        }

        customer[field] = value;
    }

    return customer;
}

async function loginAsAdmin() {
    const agent = request.agent(app);
    const res = await agent.post("/customer/login").send({username: admin.username, password: admin.password});
    expect(res.statusCode).toBe(302);
    expect(res.headers.location).toBe('/');
    return agent;
}

const admin = generateRandomCustomer();

describe('Customer Routes', () => {
    test('Create valid customer: /customer', async () => {
        const res = await request(app).post("/customer").send(admin);
        expect(res.statusCode).toBe(302);
        expect(res.headers.location).toBe('/customer/login');

        // Make customer admin
        try {
            db.exec("UPDATE customer SET isadmin = 1 WHERE customer_id = 1");
        } catch (err) {
            fail("db.run Error");
        }
    });

    for (const field in maxCustomerFieldLengths) {
        if (field !== "password") {
            test(`Create invalid customer (${field}): /customer`, async () => {
                const res = await request(app).post("/customer").send(generateRandomCustomer(field));
                expect(res.statusCode).toBe(400);
                expect(res.text).toContain('Error');
            });
        }
    }

    test('Test /customer/login POST with invalid credentials (username): /customer/login', async () => {
        const res = await request(app).post("/customer/login").send({username: "", password: generateRandomCustomer().password});
        expect(res.statusCode).toBe(401);
        expect(res.text).toContain('Wrong Username');
    });

    test('Test /customer/login POST with invalid credentials (password): /customer/login', async () => {
        const res = await request(app).post("/customer/login").send({username: admin.username, password: ""});
        expect(res.statusCode).toBe(401);
        expect(res.text).toContain('Wrong Password');
    });

    test('Test /customer/login POST with valid credentials: /customer/login', async () => {
        const res = await request(app).post("/customer/login").send({username: admin.username, password: admin.password});
        expect(res.statusCode).toBe(302);
        expect(res.headers.location).toBe('/');
    });
});

describe('Test NULL/empty result handling', () => {
    const urls = [
        "/customer/0/show",
        "/product/0/show",
        "/saleorder/0/show",
        "/orderdetail/0/show",
        "/promotion/0/show",
        "/customer/0/edit",
        "/product/0/edit",
        "/saleorder/0/edit",
        "/orderdetail/0/edit",
        "/promotion/0/edit"
    ];
    
    urls.forEach(function(url) {
        test('Test NULL/empty result handling: ' + url, async () => {
            const agent = await loginAsAdmin();
            const res = await agent.get(url);
            expect(res.statusCode).toBe(404);
            expect(res.text).toContain('No Record Found');
        });
    });

    const urls2 = [
        // "/customer",
        "/product",
        "/saleorder",
        "/orderdetail",
        "/promotion",
    ];

    urls2.forEach(function(url) {
        test('Test NULL/empty result handling: ' + url, async () => {
            const agent = await loginAsAdmin();
            const res = await agent.get(url);
            expect(res.statusCode).toBe(200);
            expect(res.text).toContain('No Records Available');
        });
    });
});

afterAll(async () => {
    try {
        await db.close();
        await fs.unlink("database.db");
    } catch (err) {
        console.log(err);
    }
});