import typing as t

from marshmallow import Schema as BaseSchema
from marshmallow.fields import Integer
from marshmallow.fields import URL


# schema for the detail object of validation error response
validation_error_detail_schema: t.Dict[str, t.Any] = {
    'type': 'object',
    'properties': {
        '<location>': {
            'type': 'object',
            'properties': {
                '<field_name>': {
                    'type': 'array',
                    'items': {
                        'type': 'string'
                    }
                }
            }
        }
    }
}


# schema for validation error response
validation_error_schema: t.Dict[str, t.Any] = {
    'properties': {
        'detail': validation_error_detail_schema,
        'message': {
            'type': 'string'
        },
    },
    'type': 'object'
}


# schema for generic error response
http_error_schema: t.Dict[str, t.Any] = {
    'properties': {
        'detail': {
            'type': 'object'
        },
        'message': {
            'type': 'string'
        },
    },
    'type': 'object'
}


class Schema(BaseSchema):
    """A base schema for all schemas. Equivalent to `marshmallow.Schema`.

    *Version Added: 1.2.0*
    """

    pass


class EmptySchema(Schema):
    """An empty schema used to generate empty response/schema.

    For 204 response, you can use this schema to
    generate an empty response body. For 200 response, you can use this schema
    to generate an empty response body schema.

    Example:

    ```python
    @app.delete('/foo')
    @app.output(EmptySchema, status_code=204)
    def delete_foo():
        return ''
    ```

    It equals to use `{}`:

    ```python
    @app.delete('/foo')
    @app.output({}, status_code=204)
    def delete_foo():
        return ''
    ```
    """

    pass


class PaginationSchema(Schema):
    """A schema for common pagination information."""
    page = Integer()
    per_page = Integer()
    pages = Integer()
    total = Integer()
    current = URL()
    next = URL()
    prev = URL()
    first = URL()
    last = URL()


class FileSchema(Schema):
    """A schema for file response.

    This is used to represent a file response in OpenAPI spec. If you want to
    embed a file as base64 string in the JSON body, you can use the
    `apiflask.fields.File` field instead.

    Example:

    ```python
    from apiflask.schemas import FileSchema
    from flask import send_from_directory

    @app.get('/images/<filename>')
    @app.output(
        FileSchema(type='string', format='binary'),
        content_type='image/png',
        description='An image file'
    )
    @app.doc(summary="Returns the image file")
    def get_image(filename):
        return send_from_directory(app.config['IMAGE_FOLDER'], filename)
    ```

    The output OpenAPI spec will be:

    ```yaml
    paths:
    /images/{filename}:
      get:
        summary: Returns the image file
        responses:
          '200':
            description: An image file
            content:
              image/png:
                schema:
                  type: string
                  format: binary
    ```

    *Version Added: 2.0.0*
    """
    def __init__(
        self,
        *,
        type: str = 'string',
        format: str = 'binary'
    ) -> None:
        """
        Arguments:
            type: The type of the file. Defaults to `string`.
            format: The format of the file, one of `binary` and `base64`. Defaults to `binary`.
        """
        self.type = type
        self.format = format

    def __repr__(self) -> str:
        return f'schema: \n  type: {self.type}\n  format: {self.format}'
