import mimetypes
import os

from django.core.exceptions import ValidationError
from django.utils.deconstruct import deconstructible
from django.utils.translation import ugettext_lazy as _

from . import clamav
from .utils import detect_content_type


@deconstructible
class FileExtensionValidator(object):
    message = _(
        "File extension '%(extension)s' is not allowed. "
        "Allowed extensions are: '%(allowed_extensions)s'."
    )
    code = 'invalid_extension'

    def __init__(self, allowed_extensions=None, message=None, code=None):
        self.allowed_extensions = allowed_extensions

        if message is not None:
            self.message = message

        if code is not None:
            self.code = code

    def __call__(self, value):
        extension = os.path.splitext(value.name)[1][1:].lower()

        if self.allowed_extensions is not None and extension not in self.allowed_extensions:
            raise ValidationError(
                self.message,
                code=self.code,
                params={
                    'extension': extension,
                    'allowed_extensions': ', '.join(
                        self.allowed_extensions)
                }
            )


class FileContentTypeValidator:
    message = _(
        'File has invalid content-type. '
        'Maybe file extension is equal to file content?'
    )

    code = 'invalid_content_type'

    def __init__(self, message=None, code=None):
        if message is not None:
            self.message = message

        if code is not None:
            self.code = code

    def __call__(self, file):
        __, ext = os.path.splitext(file.name)

        detected_content_type = detect_content_type(file)

        is_valid_content_type = bool(
            (
                ext in mimetypes.guess_all_extensions(file.content_type)
            ) or (
                detected_content_type == 'application/CDFV2-unknown'
                and file.content_type == mimetypes.guess_type('.doc')
            ) or (
                detected_content_type == file.content_type
            )
        )

        if not is_valid_content_type:
            raise ValidationError(
                self.message,
                code=self.code,
                params={
                    'extension': ext,
                    'content_type': file.content_type,
                    'detected_content_type': detected_content_type
                }
            )


class AntiVirusValidator:
    message = _('File is infected with %(virus)s.')

    code = 'infected'

    def __init__(self, message=None, code=None):
        if message is not None:
            self.message = message

        if code is not None:
            self.code = code

    def __call__(self, file):
        status, virus_name = clamav.scan_file(file)

        if status != 'OK':
            raise ValidationError(
                self.message,
                self.code,
                params={
                    'virus': virus_name
                }
            )
