import { describe, expect, it } from 'vitest';

import { AuthAction, Resource, Role } from '~/composables/gql/graphql.js';
import { useAuthorizationLink } from '~/composables/useAuthorizationLink.js';

// Mock window.location for the tests
Object.defineProperty(window, 'location', {
  value: {
    search: '',
  },
  writable: true,
});

describe('useAuthorizationLink', () => {
  it('should convert role scopes to form data', () => {
    const params = new URLSearchParams({
      name: 'MyApp',
      description: 'My test application',
      scopes: 'role:admin,role:viewer',
      redirect_uri: 'https://example.com/callback',
    });

    const { formData, displayAppName, hasPermissions, permissionsSummary } =
      useAuthorizationLink(params);

    expect(formData.value).toEqual({
      name: 'MyApp',
      description: 'My test application',
      roles: [Role.ADMIN, Role.VIEWER],
      customPermissions: [],
    });

    expect(displayAppName.value).toBe('MyApp');
    expect(hasPermissions.value).toBe(true);
    expect(permissionsSummary.value).toBe('2 role(s)');
  });

  it('should group resources by their action sets', () => {
    const params = new URLSearchParams({
      name: 'Docker Manager',
      scopes: 'docker:read_any,docker:update_any,vms:read_any',
    });

    const { formData, hasPermissions, permissionsSummary } = useAuthorizationLink(params);

    // docker has read_any+update_any, vms only has read_any - these should be separate groups
    expect(formData.value.customPermissions!).toHaveLength(2);

    // Find the group with just READ_ANY
    const readOnlyGroup = formData.value.customPermissions!.find(
      (p) => p.actions.length === 1 && p.actions[0] === AuthAction.READ_ANY
    );
    expect(readOnlyGroup).toBeDefined();
    expect(readOnlyGroup?.resources).toEqual([Resource.VMS]);

    // Find the group with READ_ANY and UPDATE_ANY
    const readUpdateGroup = formData.value.customPermissions!.find(
      (p) =>
        p.actions.length === 2 &&
        p.actions.includes(AuthAction.READ_ANY) &&
        p.actions.includes(AuthAction.UPDATE_ANY)
    );
    expect(readUpdateGroup).toBeDefined();
    expect(readUpdateGroup?.resources).toEqual([Resource.DOCKER]);

    expect(hasPermissions.value).toBe(true);
    expect(permissionsSummary.value).toBe('3 permission(s)');
  });

  it('should handle mixed role and permission scopes', () => {
    const params = new URLSearchParams({
      name: 'Mixed Access App',
      scopes: 'role:admin,docker:read_any,vms:*',
    });

    const { formData, hasPermissions, permissionsSummary } = useAuthorizationLink(params);

    expect(formData.value.roles).toEqual([Role.ADMIN]);
    expect(formData.value.customPermissions!).toHaveLength(2);

    // Docker should have just read permission
    const dockerGroup = formData.value.customPermissions!.find((p) =>
      p.resources.includes(Resource.DOCKER)
    );
    expect(dockerGroup).toBeDefined();
    expect(dockerGroup?.actions).toEqual([AuthAction.READ_ANY]);

    // VMs should have all CRUD permissions from wildcard
    const vmsGroup = formData.value.customPermissions!.find((p) => p.resources.includes(Resource.VMS));
    expect(vmsGroup).toBeDefined();
    expect(vmsGroup?.actions).toContain(AuthAction.CREATE_ANY);
    expect(vmsGroup?.actions).toContain(AuthAction.READ_ANY);
    expect(vmsGroup?.actions).toContain(AuthAction.UPDATE_ANY);
    expect(vmsGroup?.actions).toContain(AuthAction.DELETE_ANY);

    expect(hasPermissions.value).toBe(true);
    expect(permissionsSummary.value).toBe('1 role(s), 2 permission(s)');
  });

  it('should handle wildcard permissions correctly', () => {
    const params = new URLSearchParams({
      name: 'Full Access App',
      scopes: 'docker:*',
    });

    const { hasPermissions, permissionsSummary } = useAuthorizationLink(params);

    expect(hasPermissions.value).toBe(true);
    expect(permissionsSummary.value).toBe('1 permission(s)');
  });

  it('should handle empty scopes gracefully', () => {
    const params = new URLSearchParams({
      name: 'No Permissions App',
      scopes: '',
    });

    const { formData, hasPermissions, permissionsSummary } = useAuthorizationLink(params);

    expect(formData.value).toEqual({
      name: 'No Permissions App',
      description: '',
      roles: [],
      customPermissions: [],
    });

    expect(hasPermissions.value).toBe(false);
    expect(permissionsSummary.value).toBe('');
  });

  it('should handle app names ending with " API Key"', () => {
    const params = new URLSearchParams({
      name: 'MyApp API Key',
      scopes: 'role:viewer',
    });

    const { formData, displayAppName } = useAuthorizationLink(params);

    expect(displayAppName.value).toBe('MyApp');
    // Name should be used as-is without appending
    expect(formData.value.name).toBe('MyApp API Key');
  });

  it('should handle invalid scopes gracefully', () => {
    const params = new URLSearchParams({
      name: 'Invalid Scopes App',
      scopes: 'role:invalid_role,unknown_resource:read,docker:invalid_action',
    });

    const { hasPermissions, permissionsSummary } = useAuthorizationLink(params);

    expect(hasPermissions.value).toBe(true); // Has scopes, even if invalid
    expect(permissionsSummary.value).toBe('1 role(s), 2 permission(s)');
  });

  it('should use default values when parameters are missing', () => {
    const params = new URLSearchParams(); // Empty params

    const { formData, displayAppName } = useAuthorizationLink(params);

    expect(formData.value.name).toBe('Unknown Application');
    expect(displayAppName.value).toBe('Unknown Application');
  });

  describe('permission grouping and preservation', () => {
    it('should group multiple resources with same actions into single permission group', () => {
      const params = new URLSearchParams({
        name: 'Multi-Resource Reader',
        scopes: 'connect:read_any,disk:read_any,docker:read_any',
      });

      const { formData } = useAuthorizationLink(params);

      // All have same action (read), so should be in one group
      expect(formData.value.customPermissions!).toHaveLength(1);
      expect(formData.value.customPermissions![0]).toEqual({
        resources: [Resource.CONNECT, Resource.DISK, Resource.DOCKER],
        actions: [AuthAction.READ_ANY],
      });
    });

    it('should create separate groups for resources with different action sets', () => {
      const params = new URLSearchParams({
        name: 'Mixed Actions App',
        scopes: 'docker:read_any,docker:update_any,vms:create_any,vms:delete_any',
      });

      const { formData } = useAuthorizationLink(params);

      // Docker has read+update, VMs has create+delete - these should be separate
      expect(formData.value.customPermissions!).toHaveLength(2);

      const dockerGroup = formData.value.customPermissions!.find((p) =>
        p.resources.includes(Resource.DOCKER)
      );
      expect(dockerGroup).toBeDefined();
      expect(dockerGroup?.actions).toContain(AuthAction.READ_ANY);
      expect(dockerGroup?.actions).toContain(AuthAction.UPDATE_ANY);

      const vmsGroup = formData.value.customPermissions!.find((p) => p.resources.includes(Resource.VMS));
      expect(vmsGroup).toBeDefined();
      expect(vmsGroup?.actions).toContain(AuthAction.CREATE_ANY);
      expect(vmsGroup?.actions).toContain(AuthAction.DELETE_ANY);
    });

    it('should handle duplicate scopes correctly', () => {
      const params = new URLSearchParams({
        name: 'Duplicate Scopes App',
        scopes: 'docker:read_any,docker:read_any,vms:update_any,vms:update_any',
      });

      const { formData } = useAuthorizationLink(params);

      // Docker has read, VMs has update - different actions so separate groups
      expect(formData.value.customPermissions!).toHaveLength(2);

      const readGroup = formData.value.customPermissions!.find((p) =>
        p.actions.includes(AuthAction.READ_ANY)
      );
      expect(readGroup?.resources).toEqual([Resource.DOCKER]);

      const updateGroup = formData.value.customPermissions!.find((p) =>
        p.actions.includes(AuthAction.UPDATE_ANY)
      );
      expect(updateGroup?.resources).toEqual([Resource.VMS]);
    });

    it('should preserve wildcard expansion for resources', () => {
      const params = new URLSearchParams({
        name: 'Wildcard App',
        scopes: 'docker:*,vms:read_any',
      });

      const { formData } = useAuthorizationLink(params);

      // Docker has all CRUD, VMs has just read - different action sets so separate groups
      expect(formData.value.customPermissions!).toHaveLength(2);

      const dockerGroup = formData.value.customPermissions!.find((p) =>
        p.resources.includes(Resource.DOCKER)
      );
      expect(dockerGroup).toBeDefined();
      // Should have all CRUD actions from wildcard
      expect(dockerGroup?.actions).toContain(AuthAction.CREATE_ANY);
      expect(dockerGroup?.actions).toContain(AuthAction.READ_ANY);
      expect(dockerGroup?.actions).toContain(AuthAction.UPDATE_ANY);
      expect(dockerGroup?.actions).toContain(AuthAction.DELETE_ANY);

      const vmsGroup = formData.value.customPermissions!.find((p) => p.resources.includes(Resource.VMS));
      expect(vmsGroup).toBeDefined();
      expect(vmsGroup?.actions).toEqual([AuthAction.READ_ANY]);
    });

    it('should handle complex permission combinations', () => {
      const params = new URLSearchParams({
        name: 'Complex Permissions App',
        scopes:
          'connect:read_any,disk:read_any,docker:*,vms:update_any,vms:delete_any,dashboard:read_any',
      });

      const { formData } = useAuthorizationLink(params);

      // Should group by action sets:
      // - connect, disk, dashboard all have just read (group 1)
      // - docker has all CRUD from wildcard (group 2)
      // - vms has update+delete (group 3)
      expect(formData.value.customPermissions!).toHaveLength(3);

      // Find read-only group (connect, disk, dashboard)
      const readOnlyGroup = formData.value.customPermissions!.find(
        (p) => p.actions.length === 1 && p.actions[0] === AuthAction.READ_ANY
      );
      expect(readOnlyGroup).toBeDefined();
      expect(readOnlyGroup?.resources).toContain(Resource.CONNECT);
      expect(readOnlyGroup?.resources).toContain(Resource.DISK);
      expect(readOnlyGroup?.resources).toContain(Resource.DASHBOARD);

      // Find full CRUD group (docker with wildcard)
      const fullCrudGroup = formData.value.customPermissions!.find(
        (p) => p.actions.length === 4 && p.resources.includes(Resource.DOCKER)
      );
      expect(fullCrudGroup).toBeDefined();
      expect(fullCrudGroup?.actions).toContain(AuthAction.CREATE_ANY);
      expect(fullCrudGroup?.actions).toContain(AuthAction.READ_ANY);
      expect(fullCrudGroup?.actions).toContain(AuthAction.UPDATE_ANY);
      expect(fullCrudGroup?.actions).toContain(AuthAction.DELETE_ANY);

      // Find update+delete group (vms)
      const updateDeleteGroup = formData.value.customPermissions!.find((p) =>
        p.resources.includes(Resource.VMS)
      );
      expect(updateDeleteGroup).toBeDefined();
      expect(updateDeleteGroup?.actions).toContain(AuthAction.UPDATE_ANY);
      expect(updateDeleteGroup?.actions).toContain(AuthAction.DELETE_ANY);
    });
  });

  describe('efficient scope encoding', () => {
    it('should decode grouped scopes correctly', () => {
      const params = new URLSearchParams({
        name: 'Grouped App',
        scopes: 'docker+vms:read_any+update_any',
      });

      const { formData } = useAuthorizationLink(params);

      // Should decode to a single group with both resources and both actions
      expect(formData.value.customPermissions!).toHaveLength(1);
      expect(formData.value.customPermissions![0].resources).toContain(Resource.DOCKER);
      expect(formData.value.customPermissions![0].resources).toContain(Resource.VMS);
      expect(formData.value.customPermissions![0].actions).toContain(AuthAction.READ_ANY);
      expect(formData.value.customPermissions![0].actions).toContain(AuthAction.UPDATE_ANY);
    });

    it('should handle mixed grouped and individual scopes', () => {
      const params = new URLSearchParams({
        name: 'Mixed Grouped App',
        scopes: 'docker+vms:read_any,dashboard:update_any',
      });

      const { formData } = useAuthorizationLink(params);

      // Should have two groups: docker+vms with read, dashboard with update
      expect(formData.value.customPermissions!).toHaveLength(2);

      const readGroup = formData.value.customPermissions!.find((p) =>
        p.actions.includes(AuthAction.READ_ANY)
      );
      expect(readGroup).toBeDefined();
      expect(readGroup?.resources).toContain(Resource.DOCKER);
      expect(readGroup?.resources).toContain(Resource.VMS);

      const updateGroup = formData.value.customPermissions!.find((p) =>
        p.actions.includes(AuthAction.UPDATE_ANY)
      );
      expect(updateGroup).toBeDefined();
      expect(updateGroup?.resources).toEqual([Resource.DASHBOARD]);
    });
  });
});
