import os.path
import subprocess
import tempfile

from datetime import date, timedelta
from pathlib import Path

from django.conf import settings
from django.contrib.auth.models import Group, Permission
from django.core.files import File
from django.test import TestCase, override_settings
from django.urls import reverse
from django.utils.text import slugify

from aemo.models import (
    Bilan, Document, Famille, LibellePrestation, Personne, Role, Prestation,
    Rapport, Utilisateur
)
from aemo.tests import InitialDataMixin, TempMediaRootMixin
from aemo.utils import format_d_m_Y
from ..models import Archive

public_key_path = os.path.join(settings.BASE_DIR, 'archive/tests/crne_rsa.pub')


@override_settings(CRNE_RSA_PUBLIC_KEY=public_key_path)
class ArchiveTests(InitialDataMixin, TempMediaRootMixin, TestCase):

    def setUp(self) -> None:
        self.user = Utilisateur.objects.create_user('user', 'user@example.org', sigle='XX')
        self.create_kwargs = {
            'nom': 'John Doe',
            'unite': 'aemo',
            'date_debut': date(2021, 1, 1),
            'date_fin': date(2021, 12, 31),
            'motif_fin': 'Autre',
            'key': '',
            'pdf': 'encrypted_data'
        }

    def _create_archive(self):
        fam = Famille.objects.create_famille(nom='Haddock', equipe='aemo')
        fam.suivi.date_fin_suivi = date(2019, 1, 1)
        fam.suivi.motif_fin_suivi = 'autres'
        fam.suivi.save()
        for idx, doc_name in enumerate(['sample.docx', 'sample.doc', 'sample.pdf', 'sample.msg']):
            doc = Document(famille=fam, titre=f"Test {idx}")
            with (Path(__file__).parent / doc_name).open(mode='rb') as fh:
                doc.fichier = File(fh, name=doc_name)
                doc.save()

        self.user.user_permissions.add(Permission.objects.get(codename='can_archive'))
        self.client.force_login(self.user)
        self.client.post(reverse('archive-add', args=['aemo', fam.pk]))
        return Archive.objects.get(nom='Haddock', unite='aemo')

    def test_model_creation(self):
        arch = Archive.objects.create(**self.create_kwargs)
        self.assertEqual(arch.nom, 'John Doe')
        self.assertEqual(arch.unite, 'aemo')
        self.assertEqual(arch.date_debut, date(2021, 1, 1))
        self.assertEqual(arch.date_fin, date(2021, 12, 31))
        self.assertEqual(arch.pdf, 'encrypted_data')

    def test_sans_permission_d_archiver(self):
        fam = Famille.objects.create_famille(nom='Haddock', equipe='aemo')
        fam.suivi.date_fin_suivi = date(2019, 1, 1)
        fam.suivi.motif_fin_suivi = 'autre'
        fam.suivi.save()
        self.assertEqual(fam.can_be_archived(self.user), False)

    def test_avec_permission_d_archiver(self):
        fam = Famille.objects.create_famille(nom='Haddock', equipe='aemo')
        fam.suivi.date_fin_suivi = date(2019, 1, 1)
        fam.suivi.motif_fin_suivi = 'autre'
        fam.suivi.save()
        self.user.user_permissions.add(Permission.objects.get(codename='can_archive'))
        self.assertEqual(fam.can_be_archived(self.user), True)

    def test_archivage_aemo(self):
        famille = Famille.objects.create_famille(
            nom='Doe', equipe='aemo', rue="Rue du lac", npa='2000', localite='Paris', telephone='012 345 67 89')
        famille.suivi.date_fin_suivi = date.today() - timedelta(days=700)
        famille.suivi.motif_fin_suivi = 'autre'
        famille.suivi.save()
        file_paths = []
        for idx, doc_name in enumerate(['sample.docx', 'sample.doc', 'sample.pdf', 'sample.msg', 'sample-2.msg']):
            doc = Document(famille=famille, titre=f"Test {idx}")
            with (Path(__file__).parent / doc_name).open(mode='rb') as fh:
                doc.fichier = File(fh, name=doc_name)
                doc.save()
                file_paths.append(doc.fichier.path)
        Personne.objects.create_personne(
            famille=famille, prenom='Archibald', nom='Doe', role=Role.objects.get(nom='Père')
        )
        enfant = Personne.objects.create_personne(
            famille=famille, prenom='Gaston', nom='Doe', date_naissance=date.today() - timedelta(days=720),
            role=Role.objects.get(nom='Enfant suivi')
        )
        enfant.formation.creche = 'Les Schtroumpfs'
        enfant.formation.save()
        Bilan.objects.create(famille=famille, date=date.today())
        Rapport.objects.create(famille=famille, date=date.today(), auteur=self.user)
        prest = Prestation.objects.create(
            famille=famille, auteur=self.user, date_prestation=date.today(), duree=timedelta(hours=2),
            lib_prestation=LibellePrestation.objects.first())
        prest.intervenants.add(self.user, through_defaults={'role': Role.objects.get(nom='Référent')})
        with (Path(__file__).parent / 'sample.pdf').open(mode='rb') as fh:
            prest.fichier = File(fh, name=doc_name)
            prest.save()
            file_paths.append(prest.fichier.path)

        grp = Group.objects.get(name='aemo')
        self.user.groups.add(grp)
        self.user.user_permissions.add(
            *list(Permission.objects.filter(codename__in=['view_famille', 'change_famille', 'can_archive']))
        )
        self.assertTrue(famille.can_be_archived(self.user))
        self.client.force_login(self.user)
        response = self.client.post(reverse('archive-add', args=['aemo', famille.pk]))
        self.assertRedirects(response, reverse('suivis-termines'))

        famille.refresh_from_db()
        self.assertNotEqual(famille.nom, 'Doe')
        self.assertEqual(len(famille.nom), 10)
        self.assertEqual(famille.rue, '')
        self.assertEqual(famille.npa, '2000')
        self.assertEqual(famille.localite, 'Paris'),
        self.assertEqual(famille.telephone, '')
        self.assertEqual(famille.parents(), [])

        enfant.refresh_from_db()
        self.assertNotEqual(enfant.nom, 'Doe')
        self.assertEqual(len(famille.nom), 10)
        self.assertNotEqual(enfant.prenom, 'Gaston')
        self.assertEqual(len(famille.nom), 10)
        self.assertEqual(enfant.formation.creche, '')

        self.assertEqual(famille.documents.count(), 0)
        self.assertEqual(famille.bilans.count(), 0)
        self.assertEqual(famille.rapports.count(), 0)
        for prest in famille.prestations.all():
            self.assertEqual(prest.texte, '')
            self.assertEqual(prest.duree, timedelta(hours=2))
            self.assertEqual(bool(prest.fichier), False)

        for path in file_paths:
            self.assertFalse(Path(path).exists())

        arch = Archive.objects.get(nom='Doe', unite='aemo')
        self.assertTrue(os.path.exists(arch.pdf.path))
        self.assertIn(f"aemo/doe-{famille.pk}", arch.pdf.name)
        self.assertTrue(famille.prestations.exists())

        # Cannot be archived a second time:
        response = self.client.post(reverse('archive-add', args=['aemo', famille.pk]))
        self.assertEqual(response.status_code, 404)

    def test_decryptage_access(self):
        arch = self._create_archive()
        private_key = os.path.join(settings.BASE_DIR, 'archive/tests/crne_rsa')
        anonymous = Utilisateur.objects.create_user('anonymous', email='anonymous@example.org')
        self.client.force_login(anonymous)
        with open(private_key, 'rb') as f:
            response = self.client.post(reverse('archive-decrypt', args=[arch.pk]), data={'file': f})
            self.assertEqual(response.status_code, 403)

        anonymous.user_permissions.add(Permission.objects.get(codename='can_archive'))
        self.client.force_login(anonymous)
        with open(private_key, 'rb') as f:
            response = self.client.post(reverse('archive-decrypt', args=[arch.pk]), data={'file': f})
            self.assertEqual(response.status_code, 200)

    def test_decryptage_aemo(self):
        arch = self._create_archive()
        private_key = os.path.join(settings.BASE_DIR, 'archive/tests/crne_rsa')

        self.client.force_login(self.user)
        response = self.client.post(reverse('archive-decrypt', args=[arch.pk]), data={'file': ''})
        self.assertEqual(response.context['form'].errors, {'file': ['Ce champ est obligatoire.']})

        with open(private_key, 'rb') as f:
            response = self.client.post(reverse('archive-decrypt', args=[arch.pk]), data={'file': f})
            self.assertEqual(
                response.get('Content-Disposition'),
                f"attachment; filename={slugify(arch.nom)}.pdf"
            )

        with tempfile.NamedTemporaryFile(delete=True, mode='wb') as fh:
            fh.write(response.content)
            subprocess.run(['pdftotext', fh.name], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            with open(f'{fh.name}.txt', 'r') as f:
                content = f.read()

            self.assertIn('Historique', content)
            self.assertIn('Fin du suivi le :\n\n01.01.2019', content)
            self.assertIn('Motif de fin de suivi :\n\nAutres', content)
            self.assertIn(f"Date d'archivage :\n\n{format_d_m_Y(date.today())}", content)
            self.assertIn('Famille Haddock', content)
            self.assertIn('Informations', content)
            self.assertIn("Fichier docx d’exemple", content)
            self.assertIn("Exemple de fichier doc", content)
            self.assertIn("Fichier pdf d’exemple", content)
            self.assertIn("Kind regards", content)
