"""
Plugin for using Google Sheets to override scores for test cases
"""

import json
import tempfile
import gspread
import pandas as pd

from .. import PluginCollection
from ..abstract_plugin import AbstractOtterPlugin


class GoogleSheetsGradeOverride(AbstractOtterPlugin):
    """
    Otter plugin for overriding test case scores with values in a Google Sheet on Gradescope. Uses 
    provided Google Service Account credentials to pull in the spreadsheet as a dataframe and edits
    test case scores by matching on the Gradescope assignment ID, student email, and test case name.

    Implements the ``during_generate``, ``before_grading``, and ``after_grading`` events. **Make sure 
    to list this plugin as ``otter.plugins.builtin.GoogleSheetsGradeOverride``, otherwise the 
    ``during_generate`` event of this plugin will not work.**
    
    The google sheet should have the following format:

    =============== ================== =========== ======== ==============
     Assignment ID   Email              Test Case   Points   PDF          
    =============== ================== =========== ======== ==============
     123456          student@univ.edu   q1a - 1     1        false        
    =============== ================== =========== ======== ==============

    ``Assignment ID`` should be the ID of the assignment on Gradescope, ``Email`` should be the email
    address corresponding to the student's Gradescope account, ``Test Case`` should be the name of
    the test case, and ``Points`` should be the number of points that the student should be assigned.
    ``PDF`` should be ``false`` if the student's PDF should *not* be regenerated during this run of
    the autograder.
    """

    IMPORTABLE_NAME = "otter.plugins.builtin.GoogleSheetsGradeOverride"

    def _load_df(self):
        """
        Uses the Google Sheets API credentials stored in ``self.plugin_config`` to read in the
        sheet using ``pandas``.

        Returns:
            ``pandas.core.frame.DataFrame``: the sheet as a dataframe
        """
        oauth_json = self.plugin_config["service_account_credentials"]
        with tempfile.NamedTemporaryFile(mode="w+", suffix=".json") as ntf:
            json.dump(oauth_json, ntf)
            ntf.seek(0)

            gc = gspread.service_account(filename=ntf.name)
        
        sheet_url = self.plugin_config["sheet_url"]
        sheet = gc.open_by_url(sheet_url)
        worksheet = sheet.get_worksheet(0)
        data = worksheet.get_all_values()
        colnames = data.pop(0)

        self._df = pd.DataFrame(data, columns=colnames)

    @property
    def df(self):
        """
        The grade override information dataframe
        """
        if not hasattr(self, "_df") or self._df is None:
            self._load_df()
        return self._df

    def after_grading(self, results):
        """
        Modifies the results of grading by pulling in the Google Sheet as a dataframe and updating 
        the scores for the test cases found.

        Args:
            results (``otter.test_files.GradingResults``): the results of grading
        """
        df = self.df.copy()
        df = df[
            df["Email"].isin([user["email"] for user in self.submission_metadata["users"]]) & \
            (df["Assignment ID"] == str(self.submission_metadata["assignment"]["id"]))
        ]
        for _, row in df.iterrows():
            results.update_result(row["Test Case"], score=row["Points"])

    def during_generate(self, otter_config):
        """
        Takes a path to Google Service Account credentials stored in this plugin's config as key
        ``credentials_json_path`` and extracts the data from that file into the plugin's config as key
        ``service_account_credentials``.

        Args:
            otter_config (``dict``): the parsed Otter configuration JSON file
        """
        cfg_idx = [self.IMPORTABLE_NAME in c.keys() for c in otter_config["plugins"] if isinstance(c, dict)].index(True)
        creds_path = otter_config["plugins"][cfg_idx][self.IMPORTABLE_NAME]["credentials_json_path"]
        with open(creds_path) as f:
            creds = json.load(f)
        otter_config["plugins"][cfg_idx][self.IMPORTABLE_NAME]["service_account_credentials"] = creds

    def before_grading(self, options):
        """
        Controls whether or not a PDF is generated by setting the ``token`` key of the grading options
        to ``None`` if any of the rows in the dataframe matching this (email, assignment ID) tuple
        indicate that a PDF should not be generated.

        Args:
            options (``dict``): the grading configurations
        """
        df = self.df.copy()
        df = df[
            df["Email"].isin([user["email"] for user in self.submission_metadata["users"]]) & \
            (df["Assignment ID"] == str(self.submission_metadata["assignment"]["id"]))
        ]

        for _, row in df.iterrows():
            generate_pdf = row["PDF"].strip().lower()
            if generate_pdf in ["n", "false", "f", "0"]:
                options["token"] = None
