const assert = require('node:assert/strict');
const {assertExists} = require('../../../utils/assertions');
const supertest = require('supertest');
const ObjectId = require('bson-objectid').default;
const testUtils = require('../../../utils');
const config = require('../../../../core/shared/config');
const localUtils = require('./utils');
let request;

describe('User API', function () {
    describe('As Owner', function () {
        let otherAuthor;

        beforeAll(async function () {
            await localUtils.startGhost();
            request = supertest.agent(config.get('url'));

            // create inactive user
            const authorRole = testUtils.DataGenerator.Content.roles[2].name;
            otherAuthor = await testUtils.createUser({
                user: testUtils.DataGenerator.forKnex.createUser({email: 'test+3@ghost.org'}),
                role: authorRole
            });

            // by default we login with the owner
            await localUtils.doAuth(request);
        });

        describe('Read', function () {
            it('can\'t retrieve non existent user by id', async function () {
                await request.get(localUtils.API.getApiQuery('users/' + ObjectId().toHexString() + '/'))
                    .set('Origin', config.get('url'))
                    .set('Accept', 'application/json')
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(404)
                    .expect(function (res) {
                        assert.equal(res.headers['x-cache-invalidate'], undefined);
                        const jsonResponse = res.body;
                        assertExists(jsonResponse);
                        assertExists(jsonResponse.errors);
                        testUtils.API.checkResponseValue(jsonResponse.errors[0], [
                            'message',
                            'context',
                            'type',
                            'details',
                            'property',
                            'help',
                            'code',
                            'id',
                            'ghostErrorCode'
                        ]);
                    });
            });

            it('can\'t retrieve non existent user by slug', async function () {
                await request.get(localUtils.API.getApiQuery('users/slug/blargh/'))
                    .set('Origin', config.get('url'))
                    .set('Accept', 'application/json')
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(404)
                    .expect(function (res) {
                        assert.equal(res.headers['x-cache-invalidate'], undefined);
                        const jsonResponse = res.body;
                        assertExists(jsonResponse);
                        assertExists(jsonResponse.errors);
                        testUtils.API.checkResponseValue(jsonResponse.errors[0], [
                            'message',
                            'context',
                            'type',
                            'details',
                            'property',
                            'help',
                            'code',
                            'id',
                            'ghostErrorCode'
                        ]);
                    });
            });
        });

        describe('Edit', function () {
            it('can change the other users password', async function () {
                await request.put(localUtils.API.getApiQuery('users/password/'))
                    .set('Origin', config.get('url'))
                    .send({
                        password: [{
                            newPassword: 'superSecure',
                            ne2Password: 'superSecure',
                            user_id: otherAuthor.id
                        }]
                    })
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(200);
            });
        });

        describe('Destroy', function () {
            beforeAll(async function () {
                // login with the owner
                request = supertest.agent(config.get('url'));
                await localUtils.doAuth(request);
            });

            it('[failure] Destroy unknown user id', async function () {
                await request
                    .delete(localUtils.API.getApiQuery('users/' + ObjectId().toHexString()))
                    .set('Origin', config.get('url'))
                    .expect(404);
            });

            it('Destroy known user and reassign post tags', async function () {
                const otherAuthorPost = await testUtils.createPost({
                    post: {
                        tags: [{
                            slug: 'existing-tag'
                        }, {
                            slug: 'second-one'
                        }],
                        authors: [otherAuthor]
                    }
                });

                await request
                    .delete(localUtils.API.getApiQuery(`users/${otherAuthor.id}`))
                    .set('Origin', config.get('url'))
                    .expect(200);

                const tags = await otherAuthorPost.related('tags').fetch();
                assert.equal(tags.length, 3);
                // user deletion results in a second tag being added with sort_order of 0, putting it at index 1 instead of at the end (index 2)
                assert.equal(tags.models[1].get('slug'), `hash-${otherAuthor.slug}`);
                assert.equal(tags.models[1].get('name'), `#${otherAuthor.slug}`);
            });
        });
    });

    describe('As Editor', function () {
        let editor;

        beforeAll(async function () {
            await localUtils.startGhost();
            request = supertest.agent(config.get('url'));
            // create editor
            editor = await testUtils.createUser({
                user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
                role: testUtils.DataGenerator.Content.roles[1].name
            });

            request.user = editor;
            // by default we login with the owner
            await localUtils.doAuth(request);
        });

        describe('success cases', function () {
            it('can edit himself', async function () {
                await request.put(localUtils.API.getApiQuery('users/' + editor.id + '/'))
                    .set('Origin', config.get('url'))
                    .send({
                        users: [{id: editor.id, name: 'test'}]
                    })
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(200);
            });
        });

        describe('error cases', function () {
            it('can\'t edit the owner', async function () {
                await request.put(localUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/'))
                    .set('Origin', config.get('url'))
                    .send({
                        users: [{
                            id: testUtils.DataGenerator.Content.users[0].id
                        }]
                    })
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(403);
            });

            it('Cannot transfer ownership to any other user', async function () {
                await request
                    .put(localUtils.API.getApiQuery('users/owner'))
                    .set('Origin', config.get('url'))
                    .send({
                        owner: [{
                            id: testUtils.getExistingData().users[1].id
                        }]
                    })
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(403);
            });
        });
    });

    describe('As Author', function () {
        let author;

        beforeAll(async function () {
            await localUtils.startGhost();
            request = supertest.agent(config.get('url'));
            // create author
            author = await testUtils.createUser({
                user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}),
                role: testUtils.DataGenerator.Content.roles[2].name
            });

            request.user = author;
            // by default we login with the owner
            await localUtils.doAuth(request);
        });

        describe('success cases', function () {
            it('can edit himself', async function () {
                await request.put(localUtils.API.getApiQuery('users/' + author.id + '/'))
                    .set('Origin', config.get('url'))
                    .send({
                        users: [{id: author.id, name: 'test'}]
                    })
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(200);
            });
        });

        describe('error cases', function () {
            it('can\'t edit the owner', async function () {
                await request.put(localUtils.API.getApiQuery('users/' + testUtils.DataGenerator.Content.users[0].id + '/'))
                    .set('Origin', config.get('url'))
                    .send({
                        users: [{
                            id: testUtils.DataGenerator.Content.users[0].id
                        }]
                    })
                    .expect('Content-Type', /json/)
                    .expect('Cache-Control', testUtils.cacheRules.private)
                    .expect(403);
            });
        });
    });
});
