# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.

########################################################################
# OPENAPI-URI: /api/views
########################################################################
# delete:
#   requestBody:
#     content:
#       application/json:
#         schema:
#           $ref: '#/components/schemas/editView'
#     description: View to delete
#     required: true
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/ActionCompleted'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Delete a new view
# get:
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/ViewList'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Fetches a list of all views (filters) for this user
# patch:
#   requestBody:
#     content:
#       application/json:
#         schema:
#           $ref: '#/components/schemas/editView'
#     description: New source data to set
#     required: true
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/ActionCompleted'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Edit an existing source
# post:
#   requestBody:
#     content:
#       application/json:
#         schema:
#           $ref: '#/components/schemas/defaultWidgetArgs'
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/ViewList'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Fetches a list of all views (filters) for this user
# put:
#   requestBody:
#     content:
#       application/json:
#         schema:
#           $ref: '#/components/schemas/editView'
#     description: New view data to add
#     required: true
#   responses:
#     '200':
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/ActionCompleted'
#       description: 200 Response
#     default:
#       content:
#         application/json:
#           schema:
#             $ref: '#/components/schemas/Error'
#       description: unexpected error
#   security:
#   - cookieAuth: []
#   summary: Add a new view
#
########################################################################


"""
This is the views (filters) list handler for Kibble
"""

import hashlib
import json
import re
import time


def run(API, environ, indata, session):

    # We need to be logged in for this!
    if not session.user:
        raise API.exception(403, "You must be logged in to use this API endpoint! %s")

    method = environ["REQUEST_METHOD"]
    dOrg = session.user["defaultOrganisation"] or "apache"

    # Are we adding a view?
    if method == "PUT":
        viewID = hashlib.sha224(
            ("%s-%s-%s" % (time.time(), session.user["email"], dOrg)).encode("utf-8")
        ).hexdigest()
        sources = indata.get("sources", [])
        name = indata.get("name", "unknown view")
        public = indata.get("public", False)
        if public:
            if not (
                session.user["userlevel"] == "admin"
                or dOrg in session.user["ownerships"]
            ):
                raise API.exception(
                    403, "Only owners of an organisation may create public views."
                )
        doc = {
            "id": viewID,
            "email": session.user["email"],
            "organisation": dOrg,
            "sourceList": sources,
            "name": name,
            "created": int(time.time()),
            "publicView": public,
        }
        session.DB.ES.index(
            index=session.DB.dbname, doc_type="view", id=viewID, body=doc
        )
        yield json.dumps({"okay": True, "message": "View created"})

    # Are we editing (patching) a view?
    if method == "PATCH":
        viewID = indata.get("id")
        if viewID and session.DB.ES.exists(
            index=session.DB.dbname, doc_type="view", id=viewID
        ):
            doc = session.DB.ES.get(index=session.DB.dbname, doc_type="view", id=viewID)
            if (
                session.user["userlevel"] == "admin"
                or doc["_source"]["email"] == session.user["email"]
            ):
                sources = indata.get("sources", [])
                doc["_source"]["sourceList"] = sources
                session.DB.ES.index(
                    index=session.DB.dbname,
                    doc_type="view",
                    id=viewID,
                    body=doc["_source"],
                )
                yield json.dumps({"okay": True, "message": "View updated"})
            else:
                raise API.exception(403, "You don't own this view, and cannot edit it.")
        else:
            raise API.exception(404, "We couldn't find a view with this ID.")

    # Removing a view?
    if method == "DELETE":
        viewID = indata.get("id")
        if viewID and session.DB.ES.exists(
            index=session.DB.dbname, doc_type="view", id=viewID
        ):
            doc = session.DB.ES.get(index=session.DB.dbname, doc_type="view", id=viewID)
            if (
                session.user["userlevel"] == "admin"
                or doc["_source"]["email"] == session.user["email"]
            ):
                session.DB.ES.delete(
                    index=session.DB.dbname, doc_type="view", id=viewID
                )
                yield json.dumps({"okay": True, "message": "View deleted"})
            else:
                raise API.exception(
                    403, "You don't own this view, and cannot delete it."
                )
        else:
            raise API.exception(404, "We couldn't find a view with this ID.")

    if method in ["GET", "POST"]:
        # Fetch all views for default org

        res = session.DB.ES.search(
            index=session.DB.dbname,
            doc_type="view",
            size=5000,
            body={"query": {"term": {"email": session.user["email"]}}},
        )

        # Are we looking at someone elses view?
        if indata.get("view"):
            viewID = indata.get("view")
            if session.DB.ES.exists(
                index=session.DB.dbname, doc_type="view", id=viewID
            ):
                blob = session.DB.ES.get(
                    index=session.DB.dbname, doc_type="view", id=viewID
                )
                if (
                    blob["_source"]["email"] != session.user["email"]
                    and not blob["_source"]["publicView"]
                ):
                    blob["_source"]["name"] += (
                        " (shared by " + blob["_source"]["email"] + ")"
                    )
                    res["hits"]["hits"].append(blob)
        sources = []

        # Include public views??
        if not indata.get("sources", False):
            pres = session.DB.ES.search(
                index=session.DB.dbname,
                doc_type="view",
                size=5000,
                body={
                    "query": {
                        "bool": {
                            "must": [
                                {"term": {"publicView": True}},
                                {"term": {"organisation": dOrg}},
                            ]
                        }
                    }
                },
            )
            for hit in pres["hits"]["hits"]:
                if hit["_source"]["email"] != session.user["email"]:
                    hit["_source"]["name"] += " (shared view)"
                    res["hits"]["hits"].append(hit)

        for hit in res["hits"]["hits"]:
            doc = hit["_source"]
            if doc["organisation"] != dOrg:
                continue
            if indata.get("quick"):
                xdoc = {
                    "id": doc["id"],
                    "name": doc["name"],
                    "organisation": doc["organisation"],
                }
                sources.append(xdoc)
            else:
                sources.append(doc)

        allsources = []
        if indata.get("sources", False):
            res = session.DB.ES.search(
                index=session.DB.dbname,
                doc_type="source",
                size=5000,
                body={"query": {"term": {"organisation": dOrg}}},
            )
            for zdoc in res["hits"]["hits"]:
                doc = zdoc["_source"]
                xdoc = {
                    "sourceID": doc["sourceID"],
                    "type": doc["type"],
                    "sourceURL": doc["sourceURL"],
                }
                allsources.append(xdoc)

        JSON_OUT = {
            "views": sources,
            "sources": allsources,
            "okay": True,
            "organisation": dOrg,
        }
        yield json.dumps(JSON_OUT)
