/**
 * uat-plan/discover-endpoints — pure controller-parse tests against representative
 * SmartStack controllers (nav-driven, integration-template, route-constraint).
 */
import { describe, it, expect } from 'vitest';
import { parseController } from '../discover-endpoints.js';

const NAV_CONTROLLER = `
using Microsoft.AspNetCore.Mvc;
namespace App.Api.Controllers;

[ApiController]
[NavRoute("administration.users")]
[Microsoft.AspNetCore.Authorization.Authorize]
public class UsersController : ControllerBase
{
    [HttpGet]
    [RequirePermission(Permissions.Admin.Users.View)]
    [ProducesResponseType(typeof(PaginatedResult<UserListDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetUsers([FromQuery] UserQuery q) => Ok();

    [HttpGet("{id:guid}")]
    [RequirePermission(Permissions.Admin.Users.View)]
    public async Task<IActionResult> GetUser(Guid id) => Ok();

    [HttpPost]
    [RequirePermission(Permissions.Admin.Users.Create)]
    [ProducesResponseType(typeof(UserDetailDto), StatusCodes.Status201Created)]
    public async Task<IActionResult> Create([FromBody] CreateUserCommand c) => Ok();
}
`;

const INTEGRATION_CONTROLLER = `
[ApiController]
[ApiExplorerSettings(GroupName = "integration")]
[Route("api/v1/integration/users")]
[NavRoute("administration.users")]
[Authorize]
public class UsersController : ControllerBase
{
    [HttpGet]
    [RequirePermission(AdministrationPermissions.Users.Read)]
    public IActionResult List() => Ok();

    [HttpDelete("{id:guid}")]
    [RequirePermission(AdministrationPermissions.Users.Delete)]
    public IActionResult Delete(Guid id) => Ok();
}
`;

describe('parseController — nav-driven controller', () => {
  const eps = parseController(NAV_CONTROLLER);

  it('derives the base API route from [NavRoute] when there is no [Route]', () => {
    expect(eps.map((e) => `${e.method} ${e.route}`)).toEqual([
      'GET /api/administration/users',
      'GET /api/administration/users/{id}',
      'POST /api/administration/users',
    ]);
  });

  it('captures the controller, navRoute and the raw permission expression per action', () => {
    expect(eps[0]).toMatchObject({
      controller: 'UsersController',
      navRoute: 'administration.users',
      permissionExpr: 'Permissions.Admin.Users.View',
    });
    expect(eps[2].permissionExpr).toBe('Permissions.Admin.Users.Create');
  });

  it('does not leak a permission across actions', () => {
    // each action's RequirePermission is its own; the POST must not inherit GET's
    expect(eps[1].permissionExpr).toBe('Permissions.Admin.Users.View');
    expect(eps[2].permissionExpr).toBe('Permissions.Admin.Users.Create');
  });

  it('reads the declared success status from [ProducesResponseType] (200 list / 201 create)', () => {
    expect(eps[0].okStatus).toBe(200); // GET list declares Status200OK
    expect(eps[1].okStatus).toBeUndefined(); // GET {id} declares none here
    expect(eps[2].okStatus).toBe(201); // POST create declares Status201Created
  });
});

describe('parseController — integration controller', () => {
  const eps = parseController(INTEGRATION_CONTROLLER);

  it('prefers a literal class [Route] over [NavRoute] and strips route constraints', () => {
    expect(eps.map((e) => `${e.method} ${e.route}`)).toEqual([
      'GET /api/v1/integration/users',
      'DELETE /api/v1/integration/users/{id}',
    ]);
  });

  it('still records the navRoute for permission reconciliation', () => {
    expect(eps[0].navRoute).toBe('administration.users');
    expect(eps[1].permissionExpr).toBe('AdministrationPermissions.Users.Delete');
  });
});

describe('parseController — edges', () => {
  it('returns nothing for a file with no HTTP actions', () => {
    expect(parseController('public class Foo { }')).toEqual([]);
  });

  it('tolerates a verb with no permission attribute', () => {
    const eps = parseController(`
[NavRoute("public.health")]
public class HealthController : ControllerBase {
  [HttpGet("ping")]
  public IActionResult Ping() => Ok();
}`);
    expect(eps).toHaveLength(1);
    expect(eps[0]).toMatchObject({ method: 'GET', route: '/api/public/health/ping' });
    expect(eps[0].permissionExpr).toBeUndefined();
  });

  it('flags [AllowAnonymous] actions', () => {
    const eps = parseController(`
[NavRoute("public.health")]
public class HealthController : ControllerBase {
  [HttpGet("ping")]
  [AllowAnonymous]
  public IActionResult Ping() => Ok();
}`);
    expect(eps[0].allowAnonymous).toBe(true);
  });

  it('captures the permission even when [RequirePermission] sits ABOVE the verb (order-independent block scan)', () => {
    const eps = parseController(`
[NavRoute("a.b")]
public class BController : ControllerBase {
  [RequirePermission(Permissions.A.B.View)]
  [HttpGet]
  public IActionResult List() => Ok();
}`);
    expect(eps).toHaveLength(1);
    expect(eps[0].permissionExpr).toBe('Permissions.A.B.View');
  });
});
