import datetime
import os
import re
from decimal import Decimal
from django.conf import settings
import django.contrib.postgres.fields.jsonb
import django.core.serializers.json
import django.core.validators
from django.db import migrations, models, transaction
from django.db.models import signals
import django.db.models.deletion
import django.utils.timezone
import mrsrequest.models
from mrsstat.models import stat_update
import secrets

FORMAT_EN = '%Y-%m-%d'
FORMAT_FR = '%d/%m/%Y'


def fix_y2k(apps, schema_editor):
    Transport = apps.get_model('mrsrequest', 'Transport')
    transports = Transport.objects.filter(
        models.Q(date_depart__year__lt=100) | models.Q(date_return__year__lt=100),
        mrsrequest__status__gte=1000,
    ).select_related('mrsrequest')

    for t in transports:
        if t.date_depart.year < 100:
            print(t.mrsrequest.display_id, 'old date depart', t.date_depart)
            t.date_depart = datetime.date(
                year=t.date_depart.year + 2000,
                month=t.date_depart.month,
                day=t.date_depart.day,
            )
            print(t.mrsrequest.display_id, 'new date depart', t.date_depart)

        if t.date_return.year < 100:
            print(t.mrsrequest.display_id, 'old date return', t.date_return)
            t.date_return = datetime.date(
                year=t.date_return.year + 2000,
                month=t.date_return.month,
                day=t.date_return.day,
            )
            print(t.mrsrequest.display_id, 'new date return', t.date_return)

        t.save()


def admin_to_mrs_logentries(apps, schema_editor):
    MRSRequest = apps.get_model('mrsrequest', 'MRSRequest')
    MRSRequestLogEntry = apps.get_model('mrsrequest', 'MRSRequestLogEntry')
    LogEntry = apps.get_model('admin', 'LogEntry')
    ContentType = apps.get_model('contenttypes', 'ContentType')
    c = ContentType.objects.get_for_model(MRSRequest)
    existing = [
        str(i)
        for i in MRSRequest.objects.values_list('pk', flat=True)
    ]

    for l in LogEntry.objects.filter(content_type=c):
        if l.object_id not in existing:
            continue

        MRSRequestLogEntry.objects.create(
            mrsrequest_id=l.object_id,
            user=l.user,
            action=l.action_flag,
            datetime=l.action_time,
            comment=l.change_message,
        )


def provision(apps, schema_editor):
    if 'postgres' not in settings.DATABASES['default']['ENGINE']:
        print('Not postgres -> no JSON field')
        return
    MRSRequest = apps.get_model('mrsrequest', 'MRSRequest')
    for m in MRSRequest.objects.all():
        if not m.insured:
            continue

        m.data = {
            i: getattr(m.insured, i) for i in (
                'first_name',
                'last_name',
                'nir',
                'email',
                'birth_date',
            )
        }
        m.save()


def initial_distancevp(apps, schema_editor):
    MRSRequest = apps.get_model('mrsrequest', 'MRSRequest')
    for m in MRSRequest.objects.all():
        m.data['distancevp'] = m.distancevp
        m.save()


def convert_date(date):
    if date is None:
        return date

    try:
        # skip dates already in the right format (like after a downgrade).
        datetime.strptime(date, FORMAT_FR)
    except ValueError:
        pass
    else:
        return date

    return datetime.strptime(date, FORMAT_EN).strftime(FORMAT_FR)


def change_birth_date_format(apps, schema_editor):
    """
    From birth_date yyy-mm-dd to dd/mm/yyyy.
    """
    MRSRequest = apps.get_model('mrsrequest', 'MRSRequest')
    MRSRequestLogEntry = apps.get_model('mrsrequest', 'MRSRequestLogEntry')

    if 'sqlite' in settings.DATABASES['default']['ENGINE']:
        return  # this migration only works on pg

    requests = MRSRequest.objects.all()
    if len(requests):
        print('--- found: {} requests'.format(len(requests)))

    # request.data = an object with one birth_date
    for request in requests:
        if 'birth_date' not in request.data:
            continue

        new = convert_date(request.data['birth_date'])
        if new == request.data['birth_date']:
            continue

        request.data['birth_date'] = new
        request.save()

    logentries = MRSRequestLogEntry.objects.exclude(
        data__changed__birth_date=None
    )
    print(f'Found {len(logentries)} LogEntries to patch')
    # logentries.data.changed = dict with a list of birth_date
    for entry in logentries:
        if entry.data and 'birth_date' in entry.data.get('changed', {}):
            dates = entry.data['changed']['birth_date']
            for i, date in enumerate(dates):
                new = convert_date(date)
                if new == date:
                    continue

                dates[i] = new

        entry.save()


def provision_emailtemplate(apps, schema_editor):
    EmailTemplate = apps.get_model('mrsemail', 'EmailTemplate')
    MRSRequestLogEntry = apps.get_model('mrsrequest', 'MRSRequestLogEntry')

    templates = {
        et.subject.replace(
            '{{ display_id }}',
            '\d+',
        ).replace(
            '******** A renseigner ********',
            '.*',
        ): et for et in EmailTemplate.objects.all()
    }

    for logentry in MRSRequestLogEntry.objects.exclude(data=None):
        title = logentry.data.get('subject', None)

        if title is None:
            continue

        for subject, template in templates.items():
            if re.match(subject, title):
                logentry.emailtemplate = template
                logentry.save()
                break


class Migration(migrations.Migration):

    replaces = [('mrsrequest', '0011_insured_shift_move_to_person'), ('mrsrequest', '0012_mrsrequest_insured_shift'), ('mrsrequest', '0013_fix_y2k'), ('mrsrequest', '0014_mrsrequestlogentry'), ('mrsrequest', '0015_migrate_logentries'), ('mrsrequest', '0016_instance_data'), ('mrsrequest', '0017_expense_rename_expensevp'), ('mrsrequest', '0018_distance_rename_distancevp'), ('mrsrequest', '0019_add_bill_mode'), ('mrsrequest', '0020_add_billatp'), ('mrsrequest', '0021_save_modes'), ('mrsrequest', '0022_drop_mrsrequest_reject_template_relation'), ('mrsrequest', '0023_mrsrequest_modevp_default_false'), ('mrsrequest', '0024_nonschematic_options'), ('mrsrequest', '0025_initial_data_distancevp'), ('mrsrequest', '0026_rename_mandate_date_mandate_datevp'), ('mrsrequest', '0027_add_mrsrequest_mandate_dateatp'), ('mrsrequest', '0028_initial_data_distancevp'), ('mrsrequest', '0029_convert_birth_dates_to_fr_format'), ('mrsrequest', '0030_add_mrsrequest_confirms'), ('mrsrequest', '0031_null_expense_fields'), ('mrsrequest', '0032_remove_mrsrequest_creation_ip'), ('mrsrequest', '0033_conflicts_counter_rewrite'), ('mrsrequest', '0034_mrsrequest_token'), ('mrsrequest', '0035_cancel_mrsrequest'), ('mrsrequest', '0036_multiple_pmts'), ('mrsrequest', '0037_add_mrsrequest_suspended'), ('mrsrequest', '0038_add_mrsrequest_pel'), ('mrsrequest', '0039_mrsrequestlogentry_email_template'), ('mrsrequest', '0040_expensevp_toll_parking')]

    dependencies = [
        ('mrsrequest', '0010_decimal_digits'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ('mrsemail', '0005_counter'),
        ('person', '0002_insured_shift_move_to_person'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='mrsrequest',
            name='insured_shift',
        ),
        migrations.RenameField(
            model_name='mrsrequest',
            old_name='expense',
            new_name='expensevp',
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='expensevp',
            field=models.DecimalField(decimal_places=2, default=0, help_text='Somme totale des frais de péage et/ou de transport en commun (en € TTC)', max_digits=6, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Frais de péage et/ou transports'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='insured_shift',
            field=models.NullBooleanField(default=None, verbose_name='Assuré a basculé sur cette demande'),
        ),
        migrations.RunPython(fix_y2k),
        migrations.RenameField(
            model_name='mrsrequest',
            old_name='mandate_date',
            new_name='mandate_datevp',
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='mandate_datevp',
            field=models.DateField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(datetime.date(2000, 1, 1))], verbose_name='Date de mandatement'),
        ),
        migrations.AlterField(
            model_name='transport',
            name='date_depart',
            field=models.DateField(help_text='Date du trajet aller', null=True, validators=[django.core.validators.MinValueValidator(datetime.date(2000, 1, 1)), mrsrequest.models.transport_date_validate], verbose_name='Aller'),
        ),
        migrations.AlterField(
            model_name='transport',
            name='date_return',
            field=models.DateField(help_text='Date du trajet retour', null=True, validators=[django.core.validators.MinValueValidator(datetime.date(2000, 1, 1)), mrsrequest.models.transport_date_validate], verbose_name='Retour'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='data',
            field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True, verbose_name="Formulaire tel que soumit par l'usager"),
        ),
        migrations.RunPython(provision),
        migrations.RenameField(
            model_name='mrsrequest',
            old_name='distance',
            new_name='distancevp',
        ),
        migrations.CreateModel(
            name='BillVP',
            fields=[
            ],
            options={
                'proxy': True,
                'indexes': [],
            },
            bases=('mrsrequest.bill',),
        ),
        migrations.AddField(
            model_name='bill',
            name='mode',
            field=models.CharField(choices=[('vp', 'Vehicule Personnel'), ('atp', 'Transports en commun')], db_index=True, default='vp', max_length=3),
        ),
        migrations.CreateModel(
            name='BillATP',
            fields=[
            ],
            options={
                'proxy': True,
                'indexes': [],
            },
            bases=('mrsrequest.bill',),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='distancevp',
            field=models.PositiveIntegerField(help_text='Total des kilomètres parcourus: en cas de transports aller retour, ou de transports itératifs indiquer le nombre total de km parcours. (ex.pour 2 trajets de 40 km, indiquer 80 km)', null=True, verbose_name='Distance (km)'),
        ),
        migrations.RemoveField(
            model_name='mrsrequest',
            name='reject_template',
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='modevp',
            field=models.BooleanField(blank=True, default=False, help_text='(Voiture, moto)', verbose_name='Avez vous voyagé en véhicule personnel ?'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='distancevp',
            field=models.PositiveIntegerField(blank=True, help_text='Total des kilomètres parcourus: en cas de transports aller retour, ou de transports itératifs indiquer le nombre total de km parcours. (ex.pour 2 trajets de 40 km, indiquer 80 km)', null=True, verbose_name='Distance (km)'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='expenseatp',
            field=models.DecimalField(blank=True, decimal_places=2, default=0, help_text='Somme totale des frais de transport en commun (en € TTC)', max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Frais de transports'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='expensevp',
            field=models.DecimalField(blank=True, decimal_places=2, default=0, help_text='Somme totale des frais de péage et/ou de transport en commun (en € TTC)', max_digits=6, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Frais de péage et/ou transports'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='modeatp',
            field=models.BooleanField(blank=True, default=False, help_text='(Avion, bus, métro, train, bateau…)', verbose_name='Avez vous voyagé en transports en commun ?'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='mandate_dateatp',
            field=models.DateField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(datetime.date(2000, 1, 1))], verbose_name='Date de mandatement ATP'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='mandate_datevp',
            field=models.DateField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(datetime.date(2000, 1, 1))], verbose_name='Date de mandatement VP'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='saving',
            field=models.DecimalField(decimal_places=2, editable=False, max_digits=8, null=True, verbose_name='économie'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='expensevp',
            field=models.DecimalField(blank=True, decimal_places=2, default=0, help_text='Somme totale des frais de péage et/ou de transport en commun (en € TTC)', max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Frais de péage et/ou transports'),
        ),
        migrations.RemoveField(
            model_name='mrsrequest',
            name='creation_ip',
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='conflicts_accepted',
            field=models.PositiveIntegerField(default=0, help_text='Nombre de signalements acceptés pour cette demande', verbose_name='Nb. signalements acceptés'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='conflicts_resolved',
            field=models.PositiveIntegerField(default=0, help_text='Nombre de signalements résolus avant soumission', verbose_name='Nb. signalements résolus'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='token',
            field=models.CharField(default=secrets.token_urlsafe, editable=False, max_length=255, null=True, verbose_name="Token d'authentification pour modifier la demande"),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='status',
            field=models.IntegerField(choices=[(1, 'Soumise'), (100, 'Annulée'), (999, 'Rejetée'), (1000, 'En cours de liquidation'), (2000, 'Validée')], default=1, verbose_name='Statut'),
        ),
        migrations.AlterField(
            model_name='pmt',
            name='mrsrequest',
            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='mrsrequest.MRSRequest'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='suspended',
            field=models.BooleanField(blank=True, db_index=True, default=False),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='status',
            field=models.IntegerField(choices=[(1, 'Soumise'), (100, 'Annulée'), (600, 'Suspendue'), (999, 'Rejetée'), (1000, 'En cours de liquidation'), (2000, 'Validée')], default=1, verbose_name='Statut'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='pel',
            field=models.CharField(blank=True, max_length=14, null=True, validators=[django.core.validators.RegexValidator('[a-zA-Z0-9]{14}', message='Le numéro de PMET doit comporter 14 caractères alpha numériques')], verbose_name='Numéro de PMET'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='institution',
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='institution.Institution', verbose_name='Établissement'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='status',
            field=models.IntegerField(choices=[(1, 'Soumise'), (100, 'Annulée'), (999, 'Rejetée'), (1000, 'En cours de liquidation'), (2000, 'Validée')], default=1, verbose_name='Statut'),
        ),
        migrations.CreateModel(
            name='MRSRequestLogEntry',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('datetime', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date et heure')),
                ('comment', models.TextField(blank=True, verbose_name='Commentaire')),
                ('data', django.contrib.postgres.fields.jsonb.JSONField(blank=True, encoder=django.core.serializers.json.DjangoJSONEncoder, null=True)),
                ('action', models.SmallIntegerField(choices=[(1, 'Soumise'), (2, 'Modifiée'), (3, 'Effacée'), (100, 'Annulée'), (900, 'Suspendue'), (999, 'Rejetée'), (1000, 'En cours de liquidation'), (2000, 'Validée'), (800, 'Contacté')])),
                ('mrsrequest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logentries', to='mrsrequest.MRSRequest')),
                ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Utilisateur')),
                ('emailtemplate', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='mrsemail.EmailTemplate')),
            ],
            options={
                'ordering': ('-datetime',),
                'verbose_name': 'Historique',
                'verbose_name_plural': 'Historique',
            },
        ),
        migrations.RunPython(admin_to_mrs_logentries),
        migrations.AddField(
            model_name='mrsrequest',
            name='expensevp_parking',
            field=models.DecimalField(blank=True, decimal_places=2, help_text='Somme totale des frais de stationnement (en € TTC)', max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Frais de stationnement'),
        ),
        migrations.AddField(
            model_name='mrsrequest',
            name='expensevp_toll',
            field=models.DecimalField(blank=True, decimal_places=2, help_text='Somme totale des frais de péage (en € TTC)', max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Frais de péage'),
        ),
        migrations.AlterField(
            model_name='mrsrequest',
            name='expensevp',
            field=models.DecimalField(blank=True, decimal_places=2, editable=False, help_text='Somme des frais de péage et stationnement (en € TTC)', max_digits=6, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0.00'))], verbose_name='Total des frais'),
        ),
        migrations.RunPython(initial_distancevp),
        migrations.RunPython(
            change_birth_date_format,
            lambda apps, schema_editor: True  # allow migration reveres
        ),
        migrations.RunPython(provision_emailtemplate),
    ]