import { buildContract } from '@jig-software/trest-core';
import { z } from 'zod';
import { IssuedToken, TokenPrivilege } from '../schemas';
import { QueryPagination } from './common';

export const ListTokensDto = z
  .object({
    revoked: z.boolean(),
  })
  .merge(QueryPagination)
  .partial();
export type ListTokensDto = z.infer<typeof ListTokensDto>;

export const ListTokensResultDto = IssuedToken.omit({
  token: true,
}).extend({
  tokenHash: z.string(),
});
export type ListTokensResultDto = z.infer<typeof ListTokensResultDto>;

export const GenerateTokenDto = z.object({
  audience: z.string(),
  username: z.string(),
  password: z.string(),
  allowRefresh: z.boolean(),
  lifeTime: z.number().optional(),
  privileges: z.array(TokenPrivilege).optional(),
});
export type GenerateTokenDto = z.infer<typeof GenerateTokenDto>;

export const GenerateTokenResultDto = z.object({
  token: z.string(),
});
export type GenerateTokenResultDto = z.infer<typeof GenerateTokenResultDto>;

export const GenerateTokenErrorDto = z.object({
  message: z.string(),
  code: z.number().int(),
});
export type GenerateTokenErrorDto = z.infer<typeof GenerateTokenErrorDto>;

export const RefreshTokenDto = z.object({
  token: z.string(),
});
export type RefreshTokenDto = z.infer<typeof RefreshTokenDto>;

export const tokenContract = buildContract((c) =>
  c
    .prefix('token')
    .parse((p) =>
      p.error.text(500).error.text(400).error.text(401).error.text(403),
    )
    .endpoints({
      listTokens: (e) =>
        e
          .method('GET')
          .prepare((p) => p.query(ListTokensDto))
          .parse((p) => p.success.object(200, z.array(ListTokensDto))),
      listRevokedTokens: (e) =>
        e
          .method('GET')
          .path('-/revoked')
          .parse((p) => p.success.object(200, z.array(ListTokensDto))),
      listUserTokens: (e) =>
        e
          .method('GET')
          .path(':username')
          .prepare((p) => p.param('username').query(ListTokensDto))
          .parse((p) => p.success.object(200, z.array(ListTokensDto))),
      generateToken: (e) =>
        e
          .method('POST')
          .prepare((p) => p.body.json(GenerateTokenDto))
          .parse((p) =>
            p.success
              .object(201, GenerateTokenResultDto)
              .error.object(402, GenerateTokenErrorDto),
          ),
      refreshToken: (e) =>
        e
          .method('PUT')
          .prepare((p) => p.body.json(RefreshTokenDto))
          .parse((p) => p.success.object(201, RefreshTokenDto).error.text(402)),
    }),
);
