from datetime import time, timedelta
from decimal import Decimal
from uuid import UUID

import yaml
from django.utils.safestring import SafeString
from rest_framework.exceptions import ErrorDetail
from rest_framework.renderers import BaseRenderer, JSONRenderer


class OpenApiYamlRenderer(BaseRenderer):
    media_type = 'application/vnd.oai.openapi'
    format = 'yaml'

    def render(self, data, media_type=None, renderer_context=None):
        # disable yaml advanced feature 'alias' for clean, portable, and readable output
        class Dumper(yaml.SafeDumper):
            def ignore_aliases(self, data):
                return True

        def error_detail_representer(dumper, data):
            return dumper.represent_dict({'string': str(data), 'code': data.code})
        Dumper.add_representer(ErrorDetail, error_detail_representer)

        def multiline_str_representer(dumper, data):
            scalar = dumper.represent_str(data)
            scalar.style = '|' if '\n' in data else None
            return scalar
        Dumper.add_representer(str, multiline_str_representer)

        def decimal_representer(dumper, data):
            # prevent emitting "!! float" tags on fractionless decimals
            value = str(data)
            if '.' in value:
                return dumper.represent_scalar('tag:yaml.org,2002:float', value)
            else:
                return dumper.represent_scalar('tag:yaml.org,2002:int', value)
        Dumper.add_representer(Decimal, decimal_representer)

        def timedelta_representer(dumper, data):
            return dumper.represent_str(str(data.total_seconds()))
        Dumper.add_representer(timedelta, timedelta_representer)

        def time_representer(dumper, data):
            return dumper.represent_str(data.isoformat())
        Dumper.add_representer(time, time_representer)

        def uuid_representer(dumper, data):
            return dumper.represent_str(str(data))
        Dumper.add_representer(UUID, uuid_representer)

        def safestring_representer(dumper, data):
            return dumper.represent_str(data)
        Dumper.add_representer(SafeString, safestring_representer)

        return yaml.dump(
            data,
            default_flow_style=False,
            sort_keys=False,
            allow_unicode=True,
            Dumper=Dumper
        ).encode('utf-8')


class OpenApiYamlRenderer2(OpenApiYamlRenderer):
    media_type = 'application/yaml'


class OpenApiJsonRenderer(JSONRenderer):
    media_type = 'application/vnd.oai.openapi+json'

    def get_indent(self, accepted_media_type, renderer_context):
        return super().get_indent(accepted_media_type, renderer_context) or 4


class OpenApiJsonRenderer2(OpenApiJsonRenderer):
    media_type = 'application/json'
