import router from '@adonisjs/core/services/router';
import _ from 'lodash';
import { HandlerGetOptions } from '../openapi_router_macros.js';
import { HandlerParser } from './handler_parser.js';
export default class AKOpenApiService {
    #config;
    spec = {};
    constructor(config) {
        this.#config = config;
        this.spec = this.parse(router);
    }
    parse(router) {
        return {
            openapi: '3.1.0',
            info: {
                title: this.#config.title,
                version: this.#config.version,
                description: this.baseDescription + this.#config.description,
            },
            servers: this.#config.servers,
            paths: this.parseRouter(router),
            components: {
                securitySchemes: {
                    BearerAuth: {
                        type: 'http',
                        scheme: 'bearer',
                        bearerFormat: 'JWT',
                    },
                    QueryTokenAuth: {
                        type: 'apiKey',
                        in: 'query',
                        name: 'token',
                    },
                },
            },
        };
    }
    parseRouter(router) {
        const pathItems = _(router.toJSON())
            .values()
            .flatten()
            .filter((r) => {
            const options = HandlerGetOptions(r.handler);
            return options && options?.visible;
        })
            .map(this.parseRoute.bind(this))
            .flatten();
        const resp = pathItems
            .groupBy('path')
            .mapValues((items) => _(items)
            .map((i) => i.item)
            .reduce(_.merge))
            .value();
        return resp;
    }
    parseRoute(route) {
        const path = route.pattern;
        const handlerSpec = {
            ...new HandlerParser(route.handler).parse(),
            tags: [_.capitalize(_(path).split('/').nth(1))],
        };
        const routesNoHead = _.difference(route.methods, ['HEAD']);
        const routesNoHeadNorPutIfResource = _.includes(routesNoHead, 'PUT') && _.includes(routesNoHead, 'PATCH')
            ? _.difference(routesNoHead, ['PUT'])
            : routesNoHead;
        const resp = _.map(routesNoHeadNorPutIfResource, (method) => ({
            path,
            item: {
                [_.lowerCase(method)]: handlerSpec,
            },
        }));
        return resp;
    }
    get baseDescription() {
        return `
HTTP API **serving** web app of the ${this.#config.title} service.

When content need to be sent (eg. json of an item to create it), it should be sent as a string containing the json in the request body.

# Authentication

Authentification is done by [JWT tokens](https://jwt.io/introduction).

You can get a token via signin, signup or by asking the system administrator.

### Authentication via http header

Join your token in the \`Authorization\` header prefixed by \`Bearer \`.

Example:
\`\`\`
GET https://api.dev.libelaw.fr/user
Authroziation: Bearer OFJEdjoaejdAODAEOJDeofADKAEkODEA
\`\`\`

### Authentication via query parameter

Join your token in the \`token\` query parameter.

Example:
\`\`\`
GET https://api.dev.libelaw.fr/user?token=OFJEdjoaejdAODAEOJDeofADKAEkODEA
\`\`\`


# Attachments

Attachments (file upload) is done via HTTP Multipart From Data. The name of the form-data must match the field name in the item to update.

Also, you can join json data by putting it in the \`json\` field.

Example : \`user\` with \`avatar\` property. (pay attention to \`name="avatar"\`)

\`\`\`
POST https://api.dev.libelaw.fr/user
Authroziation: Bearer OFJEdjoaejdAODAEOJDeofADKAEkODEA
Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="json"
{
    "first_name": "Jean",
    "last_name": "De Saint-Roustant",
    ...
}
--AaB03x
Content-Disposition: form-data; name="avatar"; filename="mypic.png"
Content-Type: text/plain

... contents of mypic.png ...
--AaB03x--
\`\`\`

In case of an array of files as a an item's property.
You can pass multiple multipart files. They will be added to existing files.
You can also pass a json of file representation.

Example: \`user\` with \`avatars\` property.

\`\`\`
POST https://api.dev.libelaw.fr/user
Authroziation: Bearer OFJEdjoaejdAODAEOJDeofADKAEkODEA
Content-Type: multipart/form-data; boundary=AaB03x

--AaB03x
Content-Disposition: form-data; name="json"
{
    "avatars": [{
      ...
      }, {
      ...
    }],
    ...
}
--AaB03x
Content-Disposition: form-data; name="avatars"; filename="mypic.png"
Content-Type: text/plain

... contents of mypic.png ...
--AaB03x--
Content-Disposition: form-data; name="avatars"; filename="mypic2.png"
Content-Type: text/plain

... contents of mypic2.png ...
--AaB03x--
\`\`\`


# Type formats

Date : \`\`\`2024-03-19T03:25:14.811Z\`\`\`

# Relations

Some endpoints returns model items with some or all of their relations. Whether those relations are accepted/returned or is documented by the request/return type of the endpoint.

In order to update those relations, when allowed, 2x2 situations exist. Relation can be "to one" or "to many", and its mutability can be "assign only" of "full".

## To one

### Mutability "assign only"

You can set the relation (here \`company\`) by providing a dictionary with only the primary key.

\`\`\`
{
  "firstname": "Jean",
  "company": {
    "id": ....
  },
}
\`\`\`

You can provide \`null\` to remove the relation.

### Mutability "full"

You can do the same things as "assign only", and also edit fields of the related object.

\`\`\`
{
  "company": {
    "name": "My new company name",
  },
}
\`\`\`


## To many

### Mutability "assign only"

You can set the relation (here \`preferredCities\`) by providing an array of dictionary with only the primary key.
This list will be the final result, there is no "add" or "remove" functionality, only "set".

\`\`\`
{
  "firstname": "Jean",
  "preferredCities": [{
    "id": ....
  },{
    "id": ....
  },{
    "id": ....
  }],
}
\`\`\`

You can provide an empty array or \`null\` to remove all relations.

### Mutability "full"

You can do the same things as "assign only", and also edit fields of the related object. You must provide the primary key of the relation to update, otherwise a new related object will be created.

\`\`\`
{
"preferredCities": [{
    "id": ....,
    "name": "Oslo",  // will update the city
  },{
    "name": "Paris", // will create the city
  }],
}
\`\`\`

## Secondary key

Some models have a secondary key (eg: \`Company\` => \`siren\`). You can use those key like the primary key in "to one" or "to many" relations.
Some also have tuples of secondary keys (eg: \`City\` => (\`postalCode\`, \`inseeCode\`).

eg:

\`\`\`
{
"preferredCities": [{
    "iseeCode": "10241",
    "postalCode": "19213",
  },{
    "iseeCode": "10243",
    "postalCode": "19213",
  }],
}
\`\`\`

# List filters

You can apply filters to lists. Simply add query parameters to your request. Fields and values will be used to filter the results.

\`\`\`
GET /path/?field_name_1=field_value_1&field_name_2=field_value_2
\`\`\`

Multiple operators are supported :

### Exact match

\`\`\`
GET /mission/?name=supermission
GET /mission/?morning=true
GET /mission/?price=230
\`\`\`

### Belongs to relation

\`\`\`
GET /mission/?city__id=abcd-efgh
GET /mission/?city__postalCode=75001&city__inseeCode=75101
\`\`\`

### Part of a list (strings)

If you want to filter items which field value is contained in a list of values.
Repeat \`__in\` with each value.

\`\`\`
GET /conversation/?status__in=accepted&status__in=refused
\`\`\`

### Pattern match (strings)

If you search "super" then "mega super", "superman", "super" will match.

\`\`\`
GET /mission/?name__like=super   // case sensitive
GET /mission/?name__ilike=super  // case insensitive
\`\`\`

### Comparators (dates & numbers)

\`\`\`
GET /mission/?date__lt=2024-09-01T12:32:00.000Z      // <
GET /mission/?date__lte=2024-09-01T12:32:00.000Z     // <=
GET /mission/?price__gt=230.23                        // >
GET /mission/?price__gte=230.23                       // >=
\`\`\`

### Same day

On date fields, if you want to filter items based on a day.

\`\`\`
GET /mission/?date__day=2026-11-23
GET /mission/?date__day=2026-11-23T.....
\`\`\`
`;
    }
}
//# sourceMappingURL=index.js.map