# -*- coding: utf-8 -*-
# Copyright 2022 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License")
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""API endpoints for creating, retrieving, updating, and deleting feature stages."""

from api import converters
from framework import basehandlers, permissions, rediscache
from internals import core_enums, notifier_helpers, stage_helpers
from internals.core_models import FeatureEntry, Stage
from internals.data_types import CHANGED_FIELDS_LIST_TYPE
from internals.review_models import Gate


class StagesAPI(basehandlers.EntitiesAPIHandler):
    """API for managing stages."""

    def _create_gate_for_stage(
        self, feature_id: int, stage_id: int, gate_type: int
    ) -> None:
        """Create a Gate entity for the given stage type."""
        gate = Gate(
            feature_id=feature_id,
            stage_id=stage_id,
            gate_type=gate_type,
            state=Gate.PREPARING,
        )
        gate.put()

    def _create_stage(
        self, feature_id: int, feature_type: int, stage_type: int
    ):
        """Create a new Stage entity."""
        stage = Stage(feature_id=feature_id, stage_type=stage_type)
        stage.put()
        gate_type: int | None = stage_helpers.get_gate_for_stage(
            feature_type, stage.stage_type
        )
        if gate_type is not None:
            self._create_gate_for_stage(
                stage.feature_id, stage.key.integer_id(), gate_type
            )
        return stage

    def do_get(self, **kwargs):
        """Return a specified stage based on the given ID."""
        # We get both the feature and stage for the permission checks.
        unused_fe, stage = self.get_specified_feature_and_stage(**kwargs)
        stage_dict = converters.stage_to_json_dict(stage)
        # Add extensions associated with the stage if they exist.
        extensions = stage_helpers.get_ot_stage_extensions(stage_dict['id'])
        extensions_json = [
            converters.stage_to_json_dict(es) for es in extensions
        ]
        if extensions:
            stage_dict['extensions'] = extensions_json

        return stage_dict

    def _validate_edit_permissions(self, fe: FeatureEntry, request_body: dict):
        """Validate the user has permission to submit this request."""
        user = self.get_current_user()
        is_ot_request = request_body.get('ot_action_requested', False)
        # If submitting an OT request, the user must have feature edit
        # access or be a Chromium/Google account.
        if (
            not user
            or not is_ot_request
            or not (
                user.email().endswith('@chromium.org')
                or user.email().endswith('@google.com')
            )
        ):
            # Validate the user has edit permissions and redirect if needed.
            return permissions.validate_feature_edit_permission(self, fe)
        return None

    def do_post(self, **kwargs):
        """Create a new stage."""
        feature_id_arg = kwargs.get('feature_id')
        feature = self.get_validated_entity(feature_id_arg, FeatureEntry)
        feature_id = int(feature_id_arg)

        body = self.get_json_param_dict()
        if 'stage_type' not in body:
            self.abort(400, msg='Stage type not specified.')
        stage_type = int(body['stage_type']['value'])

        redirect_resp = self._validate_edit_permissions(feature, body)
        if redirect_resp:
            return redirect_resp

        # Add the specified field values to the stage. Create a gate if needed.
        stage = self._create_stage(feature_id, feature.feature_type, stage_type)
        self.update_stage(stage, body, [])

        # Changing stage values means the cached feature should be invalidated.
        lookup_key = FeatureEntry.feature_cache_key(
            FeatureEntry.DEFAULT_CACHE_KEY, feature_id
        )
        rediscache.delete(lookup_key)

        # Return  the newly created stage ID.
        return {'message': 'Stage created.', 'stage_id': stage.key.integer_id()}

    def do_patch(self, **kwargs):
        """Update an existing stage based on the stage ID."""
        stage_id = kwargs.get('stage_id')
        stage = self.get_validated_entity(stage_id, Stage)
        feature = self.get_validated_entity(stage.feature_id, FeatureEntry)
        body = self.get_json_param_dict()

        redirect_resp = self._validate_edit_permissions(feature, body)
        if redirect_resp:
            return redirect_resp

        changed_fields: CHANGED_FIELDS_LIST_TYPE = []
        # Update specified fields.
        self.update_stage(stage, body, changed_fields)

        ot_requested = False
        for field, old_val, new_val in changed_fields:
            if field == 'ot_action_requested' and new_val is True:
                ot_requested = True
                break

        content = ''
        if (
            ot_requested
            and stage.stage_type in core_enums.OT_EXTENSION_STAGE_TYPES
        ):
            content = 'Origin trial extension requested via form.'

        notifier_helpers.notify_subscribers_and_save_amendments(
            feature, changed_fields, notify=True, content=content
        )
        # Remove all feature-related cache.
        rediscache.delete_keys_with_prefix(FeatureEntry.DEFAULT_CACHE_KEY)
        rediscache.delete_keys_with_prefix(FeatureEntry.SEARCH_CACHE_KEY)

        return {'message': 'Stage values updated.'}

    def do_delete(self, **kwargs):
        """Delete an existing stage."""
        fe, stage = self.get_specified_feature_and_stage(**kwargs)

        # Validate the user has edit permissions and redirect if needed.
        redirect_resp = permissions.validate_feature_edit_permission(self, fe)
        if redirect_resp:
            return redirect_resp

        stage.archived = True
        stage.put()

        # Remove all feature-related cache.
        rediscache.delete_keys_with_prefix(FeatureEntry.DEFAULT_CACHE_KEY)
        rediscache.delete_keys_with_prefix(FeatureEntry.SEARCH_CACHE_KEY)

        return {'message': 'Stage archived.'}
