"""This module contains import views for the ``translation`` app."""

import os
import re

import polib
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404
from django.utils.translation import ugettext as _
from django.views.generic.base import View

from mtp.apps.common.views_utils import RestrictToSuperuserOrTrainerMixin
from mtp.apps.import_export.exceptions import ImportDataError
from mtp.apps.import_export.views import XLSImportViewMixin
from mtp.apps.trainings.models import Training
from mtp.apps.translation.export_views.views import ActivityTranslationExportView, \
    TranslationExportDetailView

from ..constants import (
    CONTENT_TYPE_ID, NO_TRANSLATION, OBJECT_ID, FIELD_NAME, TEXT_TRANSLATED, TEXT_TO_TRANSLATE
)
from ..models import Translation
from ..utils import check_po_file

from .mixins import TranslationImportViewMixin


class POImportView(XLSImportViewMixin, RestrictToSuperuserOrTrainerMixin, View):
    """This view allows to import translation in xlsxfile to a po file."""

    @property
    def get_language_code(self):
        """Get selected language code passed in parameters url.

        Returns
        -------
        str
            The selected language code.
        """
        return self.kwargs['language_code_slug']

    def get_success_url(self):
        return reverse('translation:dashboard')

    def get_back_url(self):
        """Get the back url.

        Returns
        -------
        str

        """

        return self.get_success_url()

    def check_validity(self, table):
        """Check the validity of file"""
        pattern = r'^target language \[(?P<language_code>[\-\w]+)\]$'

        for data in table:
            for header in data:
                result = re.match(pattern, header)
                if result:
                    language_code = result.groupdict()['language_code']
                    if language_code and language_code.lower() != self.get_language_code.lower():
                        return False
                    return True
        return True

    def import_sheets(self):
        """Read sheets of import file. By default, only first sheet is used."""
        progression1 = self.import_data(self.reader.first_sheet_name, 'django')
        progression2 = self.import_data('iOS', 'client')
        # Save the translation progression in a Translation object.
        progression = (progression1 + progression2) / 2
        translation, created = Translation.objects.get_or_create(
            language=self.get_language_code, domain='int',
            defaults={'progression': progression})
        if not created:
            translation.progression = progression
            translation.save()

    def import_data(self, sheet_name, domain):

        # Editing the concerned .po file
        po_file = self.get_po_file(self.get_language_code, domain)

        table = self.reader.read_table(sheet_name)

        if not self.check_validity(table):
            raise ImportDataError(
                "Wrong language : we except %s language." % self.get_language_code
            )

        for data in table:
            target = po_file.find(data['text to translate'])

            target_header = 'target language [' + self.get_language_code.lower() + ']'
            if target_header in data:
                translation_text = data[target_header]
                if target and translation_text != NO_TRANSLATION:

                    # If msgid and msgstr no finish both with "\n", we have a format error
                    # So we add "\n" if is not the case
                    if target.msgid[-1] == "\n" and translation_text[-1] != "\n":
                        translation_text += "\n"

                    target.msgstr = translation_text

        self.save_po_file(po_file, self.get_language_code, domain)

        # Creation of .mo files
        # management.call_command("compilemessages", locale=[self.get_language_code])

        return po_file.percent_translated()

    def get_po_file(self, language, domain):
        """Provide a .po file with polib.

        Parameters
        ----------
        language : str
            The language wanted.
        domain : str
            The domain or pofile name wanted.
            The domain is ``django`` for CMS and ``client`` for iOS.

        Returns
        -------
        pofile
            The correspondent .po file loaded with polib.
        """
        locale_path = settings.LOCALE_PATHS[0]
        file_path = language + '/LC_MESSAGES/' + domain + '.po'
        file_path = os.path.join(locale_path, file_path)
        return polib.pofile(file_path)

    def save_po_file(self, pofile, language, domain):
        """Save a .po file with polib.

        Parameters
        ----------
        language : str
            The language wanted.
        domain : str
            The domain or pofile name wanted.
            The domain is ``django`` for CMS and ``client`` for iOS.
        """
        locale_path = settings.LOCALE_PATHS[0]
        file_path = language + '/LC_MESSAGES/' + domain + '.po'
        file_path = os.path.join(locale_path, file_path)
        check_po_file(pofile)
        pofile.save(file_path)


class ContentTranslationImportViewMixin(TranslationImportViewMixin):
    """This view allows to import text to translate for a training and this activities."""

    object = None
    exported_rows = {}
    export_created = False

    def __init__(self):
        super().__init__()
        self.main_object = None

    def import_sheets(self):
        sheet_name = self.reader.first_sheet_name
        self.import_data(sheet_name)
        for sheet_name in self.reader.workbook.get_sheet_names()[1:]:
            self.import_data(sheet_name)
        self.export_created = False

    def get_success_url(self):
        if self.main_object:
            if hasattr(self.main_object, 'get_detail_url'):
                return self.main_object.get_detail_url()
            elif hasattr(self.main_object, 'get_absolute_url'):
                return self.main_object.get_absolute_url()
            elif hasattr(self.main_object, 'get_activity_list_url'):
                return self.main_object.get_activity_list_url()
            else:
                return ''
        return self.request.META['HTTP_REFERER']

    def get_old_form_url(self):
        """Return url of old form to import translation old file with bridge."""
        return reverse(
            'translation:import-old-content',
            kwargs={'training_uuid': self.kwargs['training_uuid']}
        )

    def get_back_url(self):
        """Get the back url.

        Returns
        -------
        str

        """

        obj = self.get_translated_object
        if hasattr(obj, 'get_detail_url'):
            return obj.get_detail_url()
        elif hasattr(obj, 'get_absolute_url'):
            return obj.get_absolute_url()
        return '/'

    @property
    def get_translated_object(self):
        """Return object who want translate."""
        return get_object_or_404(Training, pk=self.kwargs['training_uuid'])

    def import_data(self, sheet_name):
        lock_translation = []

        for index, data in enumerate(self.reader.read_table(sheet_name)):

            objects = []
            locked = False

            try:
                content_type = ContentType.objects.get_for_id(data[CONTENT_TYPE_ID])
                obj = content_type.model_class().objects.get(id=data[OBJECT_ID])
                if index == 0 and not self.main_object:
                    self.main_object = obj
                objects.append(obj)
                locked = True
            except (ObjectDoesNotExist, ValueError):
                if not self.export_created:
                    self.exported_rows = {}
                    self.create_sheets()
                    self.export_created = True
                try:
                    export_data = self.exported_rows[sheet_name]
                except KeyError:
                    raise ImportDataError(
                        "Impossible to find %s." % sheet_name,
                        line=data.line
                    )
                try:
                    text = data[TEXT_TO_TRANSLATE]
                except KeyError:
                    continue
                if text in export_data:
                    for item in export_data[text]:
                        content_type_id = item['content_type_id']
                        object_id = item['object_id']
                        data[FIELD_NAME] = item['field_name']
                        content_type = ContentType.objects.get_for_id(content_type_id)
                        obj = content_type.model_class().objects.get(id=object_id)
                        if index == 0 and not self.main_object:
                            self.main_object = obj
                        objects.append(obj)

            if self.main_object and self.main_object.pk != self.get_translated_object.pk:
                raise ImportDataError(_(
                    "The submitted file does not correspond to the current training. "
                    "You have imported a wrong file."
                ))

            if TEXT_TRANSLATED in data and data[TEXT_TRANSLATED] != NO_TRANSLATION:

                for obj in objects:

                    # We truncate too long value
                    field_name = data[FIELD_NAME]
                    field = obj._meta.get_field(field_name)
                    if hasattr(field, 'max_length') and field.max_length:
                        max_length = int(field.max_length)
                        try:
                            data[TEXT_TRANSLATED] = data[TEXT_TRANSLATED][0:max_length - 1]
                        except TypeError:
                            pass

                    if (obj, field_name) not in lock_translation:
                        setattr(obj, field_name, data[TEXT_TRANSLATED])
                        obj.save()
                        if locked:
                            lock_translation.append((obj, field_name))

    def generate_rows_of_object(self, obj):
        app_label, model_name = obj._meta.app_label, obj._meta.model_name
        content_type = ContentType.objects.get(app_label=app_label, model=model_name)
        fields = self.get_fields_to_translate(obj)

        rows = {}
        for field in fields:
            text = getattr(self.get_original(obj), field.name)
            if text:
                rows[text] = {
                    'content_type_id': content_type.id,
                    'object_id': obj.id,
                    'field_name': field.name,
                }
        return rows

    def process_to_export_obj(self, sheet_name, obj, enable_nested=True):
        if obj not in self.translated_object:
            for field, field_data in self.generate_rows_of_object(obj).items():

                if sheet_name not in self.exported_rows:
                    self.exported_rows[sheet_name] = {}

                if field in self.exported_rows[sheet_name]:
                    self.exported_rows[sheet_name][field].append(field_data)
                else:
                    self.exported_rows[sheet_name][field] = [field_data]

            self.translated_object.append(obj)
            if enable_nested:
                self.parse_objects_to_export_translation(sheet_name, obj)

    def get(self, request, *args, **kwargs):
        return TranslationImportViewMixin.get(self, request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object(self.get_queryset())
        return TranslationImportViewMixin.post(self, request, *args, **kwargs)


class ActivityTranslationImportView(ContentTranslationImportViewMixin,
                                    ActivityTranslationExportView):

    @property
    def get_translated_object(self):
        return get_object_or_404(Training, pk=self.kwargs['training_uuid'])


class ContentTranslationImportView(ContentTranslationImportViewMixin, TranslationExportDetailView):

    @property
    def get_translated_object(self):
        return self.get_object()
