from dataclasses import dataclass
from http.server import SimpleHTTPRequestHandler

from byteb4rb1e.utils.io import ChunksIO


@dataclass
class HandlerOptions:
    """configuration options of the HTTP POST method handler
    """
    max_chunk_size: int = ChunksIO.max_chunk_size
    # default (in memory) buffer size in bytes (from KiB) of the sliding buffer
    # reading from the pure (unchunked) client read stream
    buffer_size: int = 512 * 1024


@dataclass
class ServerOptions:
    """configuration options of the HTTP server
    """
    handler: HandlerOptions
    hostname: str = ''
    port: int = 8000


class MultipartUploadHandler(SimpleHTTPRequestHandler):
    """Simple, yet compliant HTTP/1.0 MIME Multipart Upload Handler

        Implementation of a RFC1341 & RFC7578 compliant server for handling
        multipart uploads.

        This is meant as a utility for debugging MIME Multipart upload clients

        Support for:
            - client 'Expect' header
            - chunked transfer-encoding
    """
    media_subtypes = [
        'mixed',
        'alternative',
        'parallel',
        'digest',
        'form-data'
    ]

    def do_POST(self):
        h_content_type = self.headers.get('Content-Type')
        h_expect = self.headers.get('Expect')
        h_transfer_encoding = self.headers.get('Transfer-Encoding')

        if h_content_type == None:
            self.send_error(400, 'Missing \'Content-Type\' header')

        content_type_segments = [s.strip() for s in h_content_type.split(';')]
        try:
            media_type, media_subtype = content_type_segments[0].split('/', 1)
        except IndexError:
            self.send_error(
                400,
                'no value was supplied for \'Content-Type\' header'
            )
        except ValueError:
            self.send_error(
                400,
                'unable to parse media type and subtype from ' +
                'first (semicolon-delimited) segment of \'Content-Type\' ' +
                f'header value: {content_type_segments[0]}'
            )

        if media_type != 'multipart':
            self.send_error(
                400,
                'unsupported media type in \'Content-Type\' header value: ' +
                f'{media_type}'
            )
        elif media_subtype not in self.media_subtypes:
            self.send_error(
                400,
                'unsupported media sub-type in \'Content-Type\' header value: ' +
                f'{media_type}. Must be one of {", ".join(self.media_subtypes)}'
            )

        if h_transfer_encoding:
            if h_transfer_encoding != 'chunked':
                self.send_error(
                    501,
                    f'unable to handle transfer-encoding: {h_transfer_encoding}'
                )

        content_type_params = {v[0].strip():v[1].strip() for v in [
            s.split('=', 1) for s in content_type_segments[1:]
        ]}

        boundary = content_type_params.get('boundary', '')
        boundary_len = len(boundary)
        if boundary == '':
            self.send_error(
                400,
                'missing \'boundary\' parameter in \'Content-Type\' header field'
            )
        elif boundary_len > 70:
            self.send_error(
                400,
                '\'boundary\' parameter value in \'Content-Type\' too long. ' +
                f'Is {boundary_len} characters long, must be less than 70.'
            )

        del content_type_params['boundary']
        content_type_params_keys = content_type_params.keys()
        if len(content_type_params_keys) > 0:
            self.send_error(
                400,
                'None other than \'boundary\' parameter in \'Content-Type\'' +
                'header expected. Also received ' +
                '{\', \'.join(content_type_param_keys)}'
            )


        self.handle_expect_100()

        # read the first 4-bytes of the body to check if it has a preamble
        # indication

        # well great... curl is not RFC 1341 compliant. And RFC 1341 is asking
        # for tolerance towards non-compliant clients...

        self.send_response(200, 'OK')
        self.end_headers()

