"""
Author: William Palin
Date created: 2023-01-16
Scraper for the Decisiones del Tribunal Supremo
CourtID: pr
Court Short Name: Puerto Rico
"""

import re
from datetime import date, datetime

from dateparser import parse

from juriscraper.AbstractSite import logger
from juriscraper.lib.string_utils import titlecase
from juriscraper.OpinionSiteLinear import OpinionSiteLinear


class Site(OpinionSiteLinear):
    first_opinion_date = "1998/01/01"
    base_url = "https://poderjudicial.pr/index.php/tribunal-supremo/decisiones-del-tribunal-supremo/decisiones-del-tribunal-supremo"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.court_id = self.__module__
        self.year = date.today().year
        self.url = f"{self.base_url}-{self.year}/"
        self.status = "Published"
        self.request["verify"] = False
        self.make_backscrape_iterable(kwargs)

    def _process_html(self) -> None:
        """Process the HTML and extract opinions into self.cases

        :return: None
        """
        # The website has used 3 different HTML layouts over the years.
        # We detect the format by counting sibling TDs of the TSPR link:
        #   >=5 → middle format (2005-2022)
        #   4   → oldest format (1998-2004)
        #   0   → newest format (2023+)
        #   1-3 → false positive (non-citation link containing "TSPR")
        cells_in_tr_xpath = (
            "ancestor::tr[1]/following-sibling::tr[position() <= 5]"
        )
        cells_in_td_xpath = "ancestor::td[1]/following-sibling::td"

        for link in self.html.xpath("//a[contains(string(.), 'TSPR')]"):
            url = link.xpath("@href")[0]
            citation = link.xpath("string(.)").strip()
            sibling_td_count = len(link.xpath(cells_in_td_xpath))

            if sibling_td_count >= 5:
                # Middle format (2005-2022): single row per case
                # TDs: [citation, materia, docket, name, date, ponente]
                # >= 5 instead of == 5 because some rows on the source
                # merge two cases into a single <tr> (e.g. 2013 TSPR 87)
                cells = link.xpath(cells_in_td_xpath)
                docket = cells[1].text_content().strip()
                name = cells[2].text_content().strip()
                date_str = cells[3].text_content().strip()
            elif sibling_td_count == 4:
                # Oldest format (1998-2004): single row per case
                # TDs: [citation, docket, name, day_or_date, type]
                # The date column varies: sometimes just a day number
                # (with month from a preceding <h3>), sometimes a
                # day-month string like "21-Nov", "7/AGOSTO",
                # "26 ABRIL", or even a full date
                cells = link.xpath(cells_in_td_xpath)
                docket = cells[0].text_content().strip()
                name = cells[1].text_content().strip()
                day_field = re.sub(
                    r"\s+", " ", cells[2].text_content()
                ).strip()
                if day_field.isdigit():
                    month_h3 = link.xpath(
                        "ancestor::table[1]/preceding::h3[1]"
                    )
                    month_name = (
                        month_h3[0].text_content().strip().lower()
                        if month_h3
                        else ""
                    )
                    date_str = f"{day_field} de {month_name} de {self.year}"
                elif day_field:
                    # Append the year if not already present, so
                    # dateparser can resolve "21-Nov" → "21-Nov 2000"
                    date_str = day_field
                    if str(self.year) not in date_str:
                        date_str = f"{date_str} {self.year}"
                else:
                    date_str = ""
            elif sibling_td_count == 0:
                # Newest format (2023+): vertical layout with labeled rows
                # Rows: [Núm., Partes, Ponente, Fecha, Materia]
                cells = link.xpath(cells_in_tr_xpath)
                docket = self.extract_cell(cells, 0)
                name = self.extract_cell(cells, 1)
                date_str = cells[3].xpath(".//td")[1].text_content().strip()
            else:
                # 1-3 siblings: false positive match — a link in a
                # non-citation column whose text happens to contain
                # "TSPR" (e.g. case name "ADOPCION CITA TSPR Y PRSC")
                continue

            # "1ro" is Spanish for "primero" (first day of month),
            # which dateparser doesn't recognize
            date_str = re.sub(r"\b1ro\b", "1", date_str)
            date_obj = parse(date_str, languages=["es"])

            date_filed_is_approximate = False
            if not date_obj:
                # E.g. 2022TSPR106 has the docket number in the date
                # column instead of a date due to a website data error
                date_filed_is_approximate = True
                if self.cases:
                    fallback = self.cases[-1]["date"]
                else:
                    fallback = f"{self.year}-01-01"
                logger.info(
                    "Could not parse date for %s, using approximate date %s",
                    citation,
                    fallback,
                )
                parsed_date = fallback
            else:
                parsed_date = str(date_obj.date())

            self.cases.append(
                {
                    "name": titlecase(name),
                    "url": url,
                    "citation": citation.strip(),
                    "docket": docket,
                    "date": parsed_date,
                    "date_filed_is_approximate": date_filed_is_approximate,
                }
            )

    def extract_cell(self, cells, index) -> str:
        """Extract the value from a labeled table cell in the newest layout

        Each cell contains a label (e.g. "Núm.", "Partes") followed by the
        actual value. The number of blank lines between them varies by year,
        so blank lines are filtered out before indexing.

        :param cells: List of table row elements
        :param index: Index of the cell to extract the value from
        :return: The cell value with whitespace stripped
        """
        lines = [
            line.strip()
            for line in cells[index].text_content().split("\n")
            if line.strip()
        ]
        return lines[1] if len(lines) > 1 else lines[0]

    def make_backscrape_iterable(self, kwargs) -> None:
        """Build the year range iterable used for back-scraping

        :param kwargs: Keyword arguments optionally containing backscrape_start
            and backscrape_end as "YYYY/MM/DD" strings
        :return: None
        """
        start = kwargs.get("backscrape_start")
        end = kwargs.get("backscrape_end")

        if not start:
            return

        start_date = datetime.strptime(start, "%Y/%m/%d")
        end_date = datetime.strptime(end, "%Y/%m/%d")

        self.back_scrape_iterable = range(start_date.year, end_date.year + 1)

    def _download_backwards(self, year) -> None:
        """Download and parse opinions for a single back-scrape year

        :param year: The year to scrape
        :return: None
        """
        self.year = year
        self.url = f"{self.base_url}-{year}/"
        self.html = self._download()
        self._process_html()
