import {StaffTokenResponseType} from '@tryghost/admin-x-framework/api/staffToken';
import {expect, test} from '@playwright/test';
import {globalDataRequests} from '../../../utils/acceptance';
import {mockApi, responseFixtures, testUrlValidation} from '@tryghost/admin-x-framework/test/acceptance';

test.describe('User profile', async () => {
    test('Supports editing user profiles', async ({page}) => {
        const userToEdit = responseFixtures.users.users.find(user => user.email === 'administrator@test.com')!;

        const {lastApiRequests} = await mockApi({page, requests: {
            ...globalDataRequests,
            browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},
            editUser: {method: 'PUT', path: `/users/${userToEdit.id}/?include=roles`, response: {
                users: [{
                    ...userToEdit,
                    email: 'newadmin@test.com',
                    name: 'New Admin'
                }]
            }}
        }});

        await page.goto('/');

        const section = page.getByTestId('users');
        const activeTab = section.locator('[role=tabpanel]:not(.hidden)');

        await section.getByRole('tab', {name: 'Administrators'}).click();

        const listItem = activeTab.getByTestId('user-list-item').last();
        await listItem.hover();
        await listItem.getByRole('button', {name: 'Edit'}).click();

        const modal = page.getByTestId('user-detail-modal');

        // Validation failures

        await modal.getByLabel('Full name').fill('');
        await modal.getByRole('button', {name: 'Save & close'}).click();
        await expect(modal).toContainText('Please enter a name');

        await modal.getByLabel('Full name').fill(new Array(195).join('a'));
        await modal.getByRole('button', {name: 'Save & close'}).click();
        await expect(modal).toContainText('Name is too long');

        await modal.getByLabel('Email').fill('test');
        await modal.getByRole('button', {name: 'Save & close'}).click();
        await expect(modal).toContainText('Please enter a valid email address');

        await modal.getByLabel('Location').fill(new Array(195).join('a'));
        await modal.getByRole('button', {name: 'Save & close'}).click();
        await expect(modal).toContainText('Location is too long');

        await modal.getByLabel('Bio').fill(new Array(210).join('a'));
        await modal.getByRole('button', {name: 'Save & close'}).click();
        await expect(modal).toContainText('Bio is too long');

        await modal.getByLabel('Website').fill('not-a-website');
        await modal.getByLabel('Website').blur();
        await expect(modal).toContainText('Please enter a valid URL');

        const facebookInput = modal.getByLabel('Facebook profile');

        await testUrlValidation(
            facebookInput,
            'facebook.com/username',
            'https://www.facebook.com/username'
        );

        await testUrlValidation(
            facebookInput,
            'testuser',
            'https://www.facebook.com/testuser'
        );

        await testUrlValidation(
            facebookInput,
            'ab99',
            'https://www.facebook.com/ab99'
        );

        await testUrlValidation(
            facebookInput,
            'page/ab99',
            'https://www.facebook.com/page/ab99'
        );

        await testUrlValidation(
            facebookInput,
            'page/*(&*(%%))',
            'https://www.facebook.com/page/*(&*(%%))'
        );

        await testUrlValidation(
            facebookInput,
            'facebook.com/pages/some-facebook-page/857469375913?ref=ts',
            'https://www.facebook.com/pages/some-facebook-page/857469375913?ref=ts'
        );

        await testUrlValidation(
            facebookInput,
            'https://www.facebook.com/groups/savethecrowninn',
            'https://www.facebook.com/groups/savethecrowninn'
        );

        await testUrlValidation(
            facebookInput,
            'http://github.com/username',
            'http://github.com/username',
            'The URL must be in a format like https://www.facebook.com/yourPage'
        );

        await testUrlValidation(
            facebookInput,
            'http://github.com/pages/username',
            'http://github.com/pages/username',
            'The URL must be in a format like https://www.facebook.com/yourPage'
        );

        const twitterInput = modal.getByLabel('X (formerly Twitter) profile');

        await testUrlValidation(
            twitterInput,
            'twitter.com/username',
            'https://twitter.com/username'
        );

        await testUrlValidation(
            twitterInput,
            'testuser',
            'https://twitter.com/testuser'
        );

        await testUrlValidation(
            twitterInput,
            'http://github.com/username',
            'https://twitter.com/username'
        );

        await testUrlValidation(
            twitterInput,
            '*(&*(%%))',
            '*(&*(%%))',
            'The URL must be in a format like https://twitter.com/yourUsername'
        );

        await testUrlValidation(
            twitterInput,
            'thisusernamehasmorethan15characters',
            'thisusernamehasmorethan15characters',
            'Your Username is not a valid Twitter Username'
        );

        // Successful update

        await modal.getByLabel('Full name').fill('New Admin');
        await modal.getByLabel('Email').fill('newadmin@test.com');
        await modal.getByLabel('Slug').fill('newadmin');
        await modal.getByLabel('Location').fill('some location');
        await modal.getByLabel('Website').fill('https://example.com');
        await modal.getByLabel('Facebook profile').fill('fb');
        await modal.getByLabel('X (formerly Twitter) profile').fill('tw');
        await modal.getByLabel('Bio').fill('some bio');

        // Email notification settings

        await modal.getByLabel(/Comments/).uncheck();
        await modal.getByLabel(/New signups/).uncheck();
        await modal.getByLabel(/New paid members/).uncheck();
        await modal.getByLabel(/Paid member cancellations/).check();
        await modal.getByLabel(/Milestones/).uncheck();

        await modal.getByRole('button', {name: 'Save & close'}).click();

        await expect(modal.getByRole('button', {name: 'Saved'})).toBeVisible();

        await expect(listItem.getByText('New Admin')).toBeVisible();
        await expect(listItem.getByText('newadmin@test.com')).toBeVisible();

        expect(lastApiRequests.editUser?.body).toMatchObject({
            users: [{
                email: 'newadmin@test.com',
                name: 'New Admin',
                slug: 'newadmin',
                location: 'some location',
                website: 'https://example.com',
                facebook: 'fb',
                twitter: '@tw',
                bio: 'some bio',
                comment_notifications: false,
                free_member_signup_notification: false,
                paid_subscription_started_notification: false,
                paid_subscription_canceled_notification: true,
                milestone_notifications: false
            }]
        });
    });

    test('Supports uploading profile picture', async ({page}) => {
        const userToEdit = responseFixtures.users.users.find(user => user.email === 'owner@test.com')!;

        const {lastApiRequests} = await mockApi({page, requests: {
            ...globalDataRequests,
            browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},
            uploadImage: {method: 'POST', path: '/images/upload/', response: {images: [{url: 'http://example.com/image.png', ref: null}]}},
            editUser: {method: 'PUT', path: `/users/${userToEdit.id}/?include=roles`, response: {
                users: [{
                    ...userToEdit,
                    profile_image: 'http://example.com/image.png',
                    cover_image: 'http://example.com/image.png'
                }]
            }}
        }});

        await page.goto('/');

        const section = page.getByTestId('users');

        const wrapper = section.getByTestId('owner-user');
        await wrapper.hover();
        await wrapper.getByRole('button', {name: 'View profile'}).click();

        // Upload profile picture

        const modal = page.getByTestId('user-detail-modal');

        const profileFileChooserPromise = page.waitForEvent('filechooser');

        await modal.locator('label[for=avatar]').click();

        const profileFileChooser = await profileFileChooserPromise;
        await profileFileChooser.setFiles(`${__dirname}/../../../utils/images/image.png`);

        await expect(modal.locator('#avatar')).toHaveAttribute('src', 'http://example.com/image.png');

        // Upload cover image

        const coverFileChooserPromise = page.waitForEvent('filechooser');

        await modal.locator('label[for=cover-image]').click();

        const coverFileChooser = await coverFileChooserPromise;
        await coverFileChooser.setFiles(`${__dirname}/../../../utils/images/image.png`);

        await expect(modal.locator('#cover-image')).toHaveAttribute('src', 'http://example.com/image.png');

        // Save the user

        await modal.getByRole('button', {name: 'Save'}).click();

        await expect(modal.getByRole('button', {name: 'Saved'})).toBeVisible();

        expect(lastApiRequests.editUser?.body).toMatchObject({
            users: [{
                email: 'owner@test.com',
                profile_image: 'http://example.com/image.png',
                cover_image: 'http://example.com/image.png'
            }]
        });
    });

    test('Does not show email notification options for non-admin users', async ({page}) => {
        const userToEdit = responseFixtures.users.users.find(user => user.email === 'editor@test.com')!;

        await mockApi({page, requests: {
            ...globalDataRequests,
            browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},
            editUser: {method: 'PUT', path: `/users/${userToEdit.id}/?include=roles`, response: {
                users: [{
                    ...userToEdit,
                    email: 'newadmin@test.com',
                    name: 'New Admin'
                }]
            }}
        }});

        await page.goto('/');

        const section = page.getByTestId('users');
        const activeTab = section.locator('[role=tabpanel]:not(.hidden)');

        await section.getByRole('tab', {name: 'Editors'}).click();

        const listItem = activeTab.getByTestId('user-list-item').last();
        await listItem.hover();
        await listItem.getByRole('button', {name: 'Edit'}).click();

        const modal = page.getByTestId('user-detail-modal');

        await expect(modal.getByLabel(/Comments/)).toBeVisible();
        await expect(modal.getByLabel(/New signups/)).toBeHidden();
        await expect(modal.getByLabel(/New paid members/)).toBeHidden();
        await expect(modal.getByLabel(/Paid member cancellations/)).toBeHidden();
        await expect(modal.getByLabel(/Milestones/)).toBeHidden();
    });

    test('Warns when leaving without saving', async ({page}) => {
        const userToEdit = responseFixtures.users.users.find(user => user.email === 'administrator@test.com')!;

        const {lastApiRequests} = await mockApi({page, requests: {
            ...globalDataRequests,
            browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},
            editUser: {method: 'PUT', path: `/users/${userToEdit.id}/?include=roles`, response: responseFixtures.users}
        }});

        await page.goto('/');

        const section = page.getByTestId('users');
        const activeTab = section.locator('[role=tabpanel]:not(.hidden)');

        await section.getByRole('tab', {name: 'Administrators'}).click();

        const listItem = activeTab.getByTestId('user-list-item').last();
        await listItem.hover();
        await listItem.getByRole('button', {name: 'Edit'}).click();

        const modal = page.getByTestId('user-detail-modal');

        await modal.getByLabel('Full name').fill('Updated');

        await modal.getByRole('button', {name: 'Cancel'}).click();

        await expect(page.getByTestId('confirmation-modal')).toHaveText(/leave/i);

        await page.getByTestId('confirmation-modal').getByRole('button', {name: 'Leave'}).click();

        await expect(modal).toBeHidden();
        expect(lastApiRequests.editUser).toBeUndefined();
    });

    test('Supports managing staff token', async ({page}) => {
        const userToEdit = responseFixtures.users.users.find(user => user.email === 'owner@test.com')!;

        const apiKey = {
            id: 'token-id',
            created_at: '2023-01-01',
            integration_id: 'integration-id',
            last_seen_at: null,
            last_seen_version: null,
            role_id: 'role-id',
            secret: 'secret',
            type: '',
            updated_at: '2023-01-01',
            user_id: userToEdit.id
        };

        const {lastApiRequests} = await mockApi({page, requests: {
            ...globalDataRequests,
            browseUsers: {method: 'GET', path: '/users/?limit=100&include=roles', response: responseFixtures.users},

            getStaffToken: {method: 'GET', path: '/users/me/token/', response: {apiKey} satisfies StaffTokenResponseType},

            genStaffToken: {
                method: 'PUT',
                path: '/users/me/token/',
                response: {
                    apiKey: {
                        ...apiKey,
                        secret: 'new-secret'
                    }
                } satisfies StaffTokenResponseType
            }
        }});

        await page.goto('/');

        const section = page.getByTestId('users');
        const modal = page.getByTestId('user-detail-modal');

        // Can't see the staff token for other users

        const activeTab = section.locator('[role=tabpanel]:not(.hidden)');

        await section.getByRole('tab', {name: 'Administrators'}).click();

        const listItem = activeTab.getByTestId('user-list-item').last();
        await listItem.hover();
        await listItem.getByRole('button', {name: 'Edit'}).click();

        await expect(modal.getByTestId('api-keys')).toBeHidden();
        await modal.getByRole('button', {name: 'Cancel'}).click();

        // Can see and regenerate your own staff token

        const ownerItem = section.getByTestId('owner-user').last();
        await ownerItem.hover();
        await ownerItem.getByRole('button', {name: 'View profile'}).click();

        await expect(modal.getByTestId('api-keys')).toContainText('token-id:secret');

        await modal.getByTestId('api-keys').hover();
        await modal.getByTestId('api-keys').getByRole('button', {name: 'Regenerate'}).click();

        await page.getByTestId('confirmation-modal').getByRole('button', {name: 'Regenerate your Staff Access Token'}).click();

        await expect(modal.getByTestId('api-keys')).toContainText('token-id:new-secret');

        expect(lastApiRequests.genStaffToken).toBeTruthy();
    });
});
