from django.conf import settings
from django.core.cache import cache
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.db import models
from django.shortcuts import redirect
from django.utils.translation import gettext_lazy
from modelcluster.fields import ParentalKey
from wagtail.admin.edit_handlers import (
    FieldPanel,
    HelpPanel,
    InlinePanel,
    MultiFieldPanel,
    PublishingPanel,
    StreamFieldPanel,
)
from wagtail.core import blocks
from wagtail.core.fields import RichTextField, StreamField
from wagtail.core.models import Orderable, Page
from wagtail.images.blocks import ImageChooserBlock
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtailmetadata.models import MetadataPageMixin

from tuning import help

from .forms import DonateForm
from .utils import get_donated_amount_from_api


class SubpageMixin:
    """Must be used in class definition before MetadataPageMixin!"""

    # flag for rendering anchor links in menu
    is_home = False

    @property
    def root_page(self):
        if not hasattr(self, "_root_page"):
            self._root_page = self.get_ancestors().type(DonateHomePage).specific().get()
        return self._root_page

    def get_meta_image(self):
        return self.search_image or self.root_page.get_meta_image()


class DonateFormMixin(models.Model):
    """Pages which has donate form. Must be in class definition before Page!"""

    portal_project_id = models.IntegerField(
        "ID projektu v darovacím portálu", blank=True, null=True
    )

    class Meta:
        abstract = True

    def serve(self, request):
        if request.method == "POST":
            form = DonateForm(request.POST)
            if form.is_valid():
                url = form.get_redirect_url()
                return redirect(url)
        return super().serve(request)

    @property
    def show_donate_form(self):
        return bool(self.portal_project_id)


class DonateFormAmountsMixin(models.Model):
    """Amounts setup for donate forms."""

    FIRST = 1
    SECOND = 2
    THIRD = 3
    FORM_CHOICES = [
        (FIRST, "první"),
        (SECOND, "druhá"),
        (THIRD, "třetí"),
    ]

    form_amount_1 = models.IntegerField("pevná částka 1", default=100)
    form_amount_2 = models.IntegerField("pevná částka 2", default=200)
    form_amount_3 = models.IntegerField("pevná částka 3", default=500)
    form_preselected = models.IntegerField(
        "výchozí částka", default=FIRST, choices=FORM_CHOICES
    )

    class Meta:
        abstract = True


def get_url(page, dest_page_type):
    try:
        return page.get_children().type(dest_page_type).live().first().get_url()
    except (Page.DoesNotExist, AttributeError):
        return "#"


class DonateHomePage(DonateFormMixin, DonateFormAmountsMixin, Page, MetadataPageMixin):
    ### FIELDS

    # lead section
    lead_title = models.CharField("hlavní nadpis", max_length=250, blank=True)
    lead_body = models.TextField("hlavní popis", blank=True)
    lead_video = models.URLField("video na youtube", blank=True, null=True)
    lead_preview = models.ForeignKey(
        "wagtailimages.Image",
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        verbose_name="náhled videa",
    )
    # support section
    support_title = models.CharField("podpoř stranu nadpis", max_length=250, blank=True)
    support_body = models.TextField("podpoř stranu popis", blank=True)
    # projects section
    project_title = models.CharField(
        "podpoř projekt nadpis", max_length=250, blank=True
    )
    project_body = models.TextField("podpoř projekt popis", blank=True)
    # regions section
    region_title = models.CharField("podpoř kraj nadpis", max_length=250, blank=True)
    region_body = models.TextField("podpoř kraj popis", blank=True)
    # settings
    facebook = models.URLField("Facebook URL", blank=True, null=True)
    instagram = models.URLField("Instagram URL", blank=True, null=True)
    twitter = models.URLField("Twitter URL", blank=True, null=True)
    flickr = models.URLField("Flickr URL", blank=True, null=True)
    matomo_id = models.IntegerField(
        "Matomo ID pro sledování návštěvnosti", blank=True, null=True
    )

    ### PANELS

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [
                FieldPanel("lead_title"),
                FieldPanel("lead_body"),
                FieldPanel("lead_video"),
                ImageChooserPanel("lead_preview"),
            ],
            "hlavní sekce",
        ),
        MultiFieldPanel(
            [FieldPanel("support_title"), FieldPanel("support_body")],
            "podpoř stranu",
        ),
        MultiFieldPanel(
            [FieldPanel("project_title"), FieldPanel("project_body")],
            "podpoř projekt",
        ),
        MultiFieldPanel(
            [FieldPanel("region_title"), FieldPanel("region_body")],
            "podpoř kraj",
        ),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(help.build(help.IMPORTANT_TITLE)),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [
        MultiFieldPanel(
            [
                FieldPanel("facebook"),
                FieldPanel("instagram"),
                FieldPanel("twitter"),
                FieldPanel("flickr"),
            ],
            "sociální sítě",
        ),
        FieldPanel("matomo_id"),
        MultiFieldPanel(
            [
                FieldPanel("portal_project_id"),
                FieldPanel("form_amount_1"),
                FieldPanel("form_amount_2"),
                FieldPanel("form_amount_3"),
                FieldPanel("form_preselected"),
            ],
            "nastavení darů",
        ),
    ]

    ### RELATIONS

    subpage_types = [
        "donate.DonateRegionIndexPage",
        "donate.DonateProjectIndexPage",
        "donate.DonateInfoPage",
        "donate.DonateTextPage",
    ]

    ### OTHERS

    # flag for rendering anchor links in menu
    is_home = True

    class Meta:
        verbose_name = "Dary"

    @property
    def root_page(self):
        return self

    @property
    def info_page_url(self):
        return get_url(self, DonateInfoPage)

    @property
    def projects_page_url(self):
        return get_url(self, DonateProjectIndexPage)

    @property
    def regions_page_url(self):
        return get_url(self, DonateRegionIndexPage)

    @property
    def has_projects(self):
        return self.get_descendants().type(DonateProjectPage).live().exists()

    def get_context(self, request):
        context = super().get_context(request)

        context["regions"] = (
            self.get_descendants().type(DonateRegionPage).live().specific()
        )

        context["projects"] = (
            self.get_descendants()
            .type(DonateProjectPage)
            .live()
            .specific()
            .order_by("-donateprojectpage__date")[:3]
        )

        return context


class DonateRegionIndexPage(Page, SubpageMixin, MetadataPageMixin):
    ### PANELS

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(help.build(help.NO_SEO_TITLE, help.NO_SEARCH_IMAGE)),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["donate.DonateHomePage"]
    subpage_types = ["donate.DonateRegionPage"]

    ### OTHERS

    class Meta:
        verbose_name = "Přehled krajů"

    def get_context(self, request):
        context = super().get_context(request)
        context["regions"] = self.get_children().live().specific()
        return context


class DonateRegionPage(
    DonateFormMixin, DonateFormAmountsMixin, Page, SubpageMixin, MetadataPageMixin
):
    ### FIELDS

    main_title = models.CharField("hlavní nadpis na stránce", max_length=250)
    body = RichTextField("obsah")

    ### PANELS

    content_panels = Page.content_panels + [
        FieldPanel("main_title"),
        FieldPanel("body", classname="full"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(
                    help.build(
                        "Pokud není zadán <strong>Titulek stránky</strong>, použije "
                        "se <strong>Hlavní nadpis</strong> (tab obsah).",
                        help.NO_SEARCH_IMAGE,
                    )
                ),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [
        MultiFieldPanel(
            [
                FieldPanel("portal_project_id"),
                FieldPanel("form_amount_1"),
                FieldPanel("form_amount_2"),
                FieldPanel("form_amount_3"),
                FieldPanel("form_preselected"),
            ],
            "nastavení darů",
        ),
    ]

    ### RELATIONS

    parent_page_types = ["donate.DonateRegionIndexPage"]
    subpage_types = ["donate.DonateTargetedDonationsPage"]

    ### OTHERS

    class Meta:
        verbose_name = "Kraj"

    def get_meta_title(self):
        return self.seo_title or self.main_title

    @property
    def targeted_donations_page_url(self):
        return get_url(self, DonateTargetedDonationsPage)

    @property
    def has_targeted_donations(self):
        return self.get_descendants().type(DonateTargetedDonationsPage).live().exists()


class DonateProjectIndexPage(Page, SubpageMixin, MetadataPageMixin):
    ### PANELS

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(help.build(help.NO_SEO_TITLE, help.NO_SEARCH_IMAGE)),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["donate.DonateHomePage"]
    subpage_types = ["donate.DonateProjectPage"]

    ### OTHERS

    class Meta:
        verbose_name = "Přehled projektů"

    def get_context(self, request):
        context = super().get_context(request)

        paginator = Paginator(
            self.get_children().live().specific().order_by("-donateprojectpage__date"),
            6,
        )

        page = request.GET.get("page")
        try:
            projects = paginator.page(page)
        except PageNotAnInteger:
            projects = paginator.page(1)
        except EmptyPage:
            projects = paginator.page(paginator.num_pages)

        context["projects"] = projects
        return context


class DonateProjectPage(
    DonateFormMixin, DonateFormAmountsMixin, Page, SubpageMixin, MetadataPageMixin
):
    ### FIELDS

    date = models.DateField("běží od")
    perex = models.TextField("krátký popis")
    body = RichTextField("obsah")
    is_new = models.BooleanField('označení "nový projekt"', default=False)
    allow_periodic_donations = models.BooleanField(
        "umožnit pravidelné dary", default=False
    )
    photo = models.ForeignKey(
        "wagtailimages.Image",
        verbose_name="fotka",
        on_delete=models.PROTECT,
        null=True,
        blank=True,
    )
    gallery = StreamField(
        [("photo", ImageChooserBlock(label="fotka"))],
        verbose_name="galerie fotek",
        blank=True,
    )
    expected_amount = models.IntegerField("očekávaná částka", blank=True, null=True)
    donated_amount = models.IntegerField("vybraná částka", blank=True, null=True)
    # we will use photo as search image
    search_image = None

    ### PANELS

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            [FieldPanel("is_new"), FieldPanel("perex"), ImageChooserPanel("photo")],
            "info do přehledu projektů",
        ),
        FieldPanel("date"),
        FieldPanel("expected_amount"),
        FieldPanel("body", classname="full"),
        StreamFieldPanel("gallery"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                HelpPanel(
                    help.build(
                        "Pokud není zadán <strong>Titulek stránky</strong>, použije "
                        "se „Podpoř projekt <strong>Název</strong>“ (tab obsah).",
                        "Pokud není zadán <strong>Popis vyhledávání</strong>, použije "
                        "se prvních 150 znaků <strong>Perexu</strong> (tab obsah).",
                    )
                ),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = [
        PublishingPanel(),
        MultiFieldPanel(
            [
                FieldPanel("portal_project_id"),
                FieldPanel("allow_periodic_donations"),
                FieldPanel("form_amount_1"),
                FieldPanel("form_amount_2"),
                FieldPanel("form_amount_3"),
                FieldPanel("form_preselected"),
            ],
            "nastavení darů",
        ),
    ]

    ### RELATIONS

    parent_page_types = ["donate.DonateProjectIndexPage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Projekt"

    def get_meta_image(self):
        return self.photo

    def get_meta_title(self):
        return self.seo_title or self.title

    def get_meta_description(self):
        if self.search_description:
            return self.search_description
        if len(self.perex) > 150:
            return str(self.perex)[:150] + "..."
        return self.perex

    def get_donated_amount(self):
        if self.portal_project_id is None:
            return 0
        # instance caching for multiple method calls during one request
        if not hasattr(self, "_donated_amount"):
            # cache portal API calls (defaults to 5 min)
            key = f"donated_amount_{self.portal_project_id}"
            amount = cache.get(key)
            if amount is None:
                amount = get_donated_amount_from_api(self.portal_project_id)
                if amount is not None:
                    # save amount into database to be used if next API calls fails
                    self.donated_amount = amount
                    self.save()
                    cache.set(key, amount, settings.DONATE_PORTAL_API_CACHE_TIMEOUT)
            self._donated_amount = self.donated_amount or 0
        return self._donated_amount

    @property
    def donated_percentage(self):
        if not self.expected_amount:
            return 0
        if self.get_donated_amount() >= self.expected_amount:
            return 100
        return round(self.get_donated_amount() / self.expected_amount * 100)

    def get_context(self, request):
        context = super().get_context(request)
        context["other_projects"] = (
            self.get_siblings(inclusive=False)
            .live()
            .specific()
            .order_by("-donateprojectpage__date")[:3]
        )
        return context


class DonateTextPage(Page, SubpageMixin, MetadataPageMixin):
    ### FIELDS

    body = RichTextField("obsah", blank=True)

    ### PANELS

    content_panels = Page.content_panels + [
        FieldPanel("body", classname="full"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(help.build(help.NO_SEO_TITLE, help.NO_SEARCH_IMAGE)),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["donate.DonateHomePage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Stránka s textem"


class DonateInfoPage(DonateFormMixin, Page, SubpageMixin, MetadataPageMixin):
    ### FIELDS

    body = RichTextField("obsah", blank=True)

    ### PANELS

    content_panels = Page.content_panels + [
        FieldPanel("body", classname="full"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(help.build(help.NO_SEO_TITLE, help.NO_SEARCH_IMAGE)),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["donate.DonateHomePage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Infostránka s formulářem"

    # use portal_project_id from home page
    @property
    def portal_project_id(self):
        return self.get_parent().specific.portal_project_id


class TargetedDonation(Orderable):
    page = ParentalKey(
        "donate.DonateTargetedDonationsPage",
        on_delete=models.CASCADE,
        related_name="targeted_donations",
    )
    is_main = models.BooleanField(
        "hlavní dar", default=False, help_text="zobrazené samostatně nahoře"
    )
    title = models.CharField("název", max_length=255)
    description = models.CharField(
        "popis",
        null=True,
        blank=True,
        max_length=255,
        help_text="zobrazí se jen u hlavních darů",
    )
    portal_project_id = models.IntegerField("ID projektu v darovacím portálu")

    panels = [
        FieldPanel("portal_project_id"),
        FieldPanel("title"),
        FieldPanel("description"),
        FieldPanel("is_main"),
    ]


class DonateTargetedDonationsPage(
    DonateFormMixin, Page, SubpageMixin, MetadataPageMixin
):
    ### FIELDS

    # page does not have specific portal_project_id
    portal_project_id = None

    ### PANELS

    content_panels = Page.content_panels + [
        MultiFieldPanel([InlinePanel("targeted_donations")], "adresné dary"),
    ]

    promote_panels = [
        MultiFieldPanel(
            [
                FieldPanel("slug"),
                FieldPanel("seo_title"),
                FieldPanel("search_description"),
                ImageChooserPanel("search_image"),
                HelpPanel(help.build(help.NO_SEO_TITLE, help.NO_SEARCH_IMAGE)),
            ],
            gettext_lazy("Common page configuration"),
        ),
    ]

    settings_panels = []

    ### RELATIONS

    parent_page_types = ["donate.DonateRegionPage"]
    subpage_types = []

    ### OTHERS

    class Meta:
        verbose_name = "Adresné dary"

    def get_context(self, request):
        context = super().get_context(request)

        try:
            selected_project_id = int(request.GET.get("p", 0))
            selected_target = self.targeted_donations.get(
                portal_project_id=selected_project_id
            )
        except (ValueError, TargetedDonation.DoesNotExist):
            selected_target = None

        if selected_target:
            context["main_targets"] = [selected_target]
            context["other_targets"] = []
            context["is_preselected"] = True
        else:
            context["main_targets"] = self.targeted_donations.filter(is_main=True)
            context["other_targets"] = self.targeted_donations.filter(is_main=False)
            context["is_preselected"] = False

        if context["main_targets"]:
            context["initial_project_id"] = context["main_targets"][0].portal_project_id
        elif context["other_targets"]:
            context["initial_project_id"] = context["other_targets"][
                0
            ].portal_project_id
        else:
            context["initial_project_id"] = 0

        return context