import json

from dasl_api import (
    CoreV1RuleSpec,
    CommonV1ObjectMeta,
    CoreV1Rule,
    CoreV1Schedule,
    CoreV1RuleSpecInput,
    CoreV1RuleSpecOutput,
    CoreV1RuleSpecMetadata,
    CoreV1RuleObservable,
    CoreV1RuleSpecCollate,
    CoreV1RuleList,
)

from dasl_client.core.base import BaseMixin
from typing import List, Optional
import dasl_api as openapi_client

from dasl_client.errors.errors import NotFoundError, handle_errors


class RuleMixin(BaseMixin):
    @handle_errors
    def get_rule(self, name: str) -> CoreV1Rule:
        """
        Get a previously created rule by name.

        :param name: The name of the rule to fetch
        :return: a CoreV1Rule object containing the spec and status of the rule.
        :raises: NotFoundError if the rule does not exist
        """
        auth = self.auth.client()
        workspace = self.auth.workspace()
        return openapi_client.CoreV1Api(auth).core_v1_get_rule(
            workspace, name
        )

    @handle_errors
    def delete_rule(self, name: str) -> None:
        """
        Delete a rule, request all deployed resource be removed as well. Note,
        this call is an instruction to ASL to delete the rule, it does not
        block until the rule has been deleted.

        :param name: The name of the rule to delete
        :raises: NotFoundError if the rule does not exist
        """
        auth = self.auth.client()
        workspace = self.auth.workspace()
        openapi_client.CoreV1Api(auth).core_v1_delete_rule(workspace, name)

    @handle_errors
    def list_rules(
        self, cursor: Optional[str] = None, limit: Optional[int] = None
    ) -> CoreV1RuleList:
        """
        List all rules in the current client.

        :param cursor: An optional identifier generated by the server. This is
                       used to fetch rules following on from prior list
                       request which returned a cursor.
        :param limit:  An optional limit on the number of rules to return.
        :return: A CoreV1RuleList containing the rules returned.
        """
        auth = self.auth.client()
        workspace = self.auth.workspace()
        return openapi_client.CoreV1Api(auth).core_v1_list_rules(
            workspace=workspace, cursor=cursor, limit=limit
        )

    @handle_errors
    def create_rule(
        self,
        name: str,
        schedule: CoreV1Schedule,
        input: CoreV1RuleSpecInput,
        output: CoreV1RuleSpecOutput,
        comment: Optional[str] = None,
        metadata: Optional[CoreV1RuleSpecMetadata] = None,
        observables: Optional[List[CoreV1RuleObservable]] = None,
        collate: Optional[CoreV1RuleSpecCollate] = None,
    ) -> CoreV1Rule:
        """
        Create a rule with the information provided in this request.
        Note, this request is an instruction and will return before the new
        rule has been created. It can take time for the changes to propagate
        and the relevant resources to be deployed.

        :param name: The name of the rule to replace
        :param schedule: The schedule to apply to the rule.
        :param input: The source table(s) anf filters for the rule.
        :param output: The output summary and context to be associated with any notables
                       generated.
        :param comment: An optional comment for the rule.
        :param metadata: Additional context information associated with the rule.
        :param observables: Any observables to when a noteable is identified.
        :param collate: Conditions under which a generate notable should be merged with
                        a prior notable instead of being treated as a separate event.
        :return: The rule object as recorded by the server.
        :raises ConflictError: if a rule with the same name already exists.
        """

        auth = self.auth.client()
        workspace = self.auth.workspace()

        spec = CoreV1RuleSpec(
            schedule=schedule,
            input=input,
            output=output,
            metadata=metadata,
            observables=observables,
            collate=collate,
        )
        meta = CommonV1ObjectMeta(name=name, comment=comment, workspace=workspace)
        request = CoreV1Rule(metadata=meta, spec=spec)
        return openapi_client.CoreV1Api(auth).core_v1_create_rule(
            workspace, request
        )

    @handle_errors
    def replace_rule(
        self,
        name: str,
        schedule: CoreV1Schedule,
        input: CoreV1RuleSpecInput,
        output: CoreV1RuleSpecOutput,
        metadata: Optional[CoreV1RuleSpecMetadata] = None,
        observables: Optional[List[CoreV1RuleObservable]] = None,
        collate: Optional[CoreV1RuleSpecCollate] = None,
    ) -> CoreV1Rule:
        """
        Replace an existing rule with the information provided in this request.
        Note, this request is an instruction and will return before the new
        rule has been created. It can take time for the changes to propagate
        and the relevant resources to be deployed.

        :param name: The name of the rule to replace
        :param schedule: The schedule to apply to the rule.
        :param input: The source table(s) anf filters for the rule.
        :param output: The output summary and context to be associated with any notables
                       generated.
        :param metadata: Additional context information associated with the rule.
        :param observables: Any observables to when a noteable is identified.
        :param collate: Conditions under which a generate notable should be merged with
                        a prior notable instead of being treated as a separate event.
        :return: The rule object as recorded by the server.
        :raises NotFoundError: if no rule with the provided name exists to replace.
        """

        auth = self.auth.client()
        workspace = self.auth.workspace()

        spec = CoreV1RuleSpec(
            schedule=schedule,
            input=input,
            output=output,
            metadata=metadata,
            observables=observables,
            collate=collate,
        )
        meta = CommonV1ObjectMeta(workspace=workspace)
        request = CoreV1Rule(metadata=meta, spec=spec)
        return openapi_client.CoreV1Api(auth).core_v1_replace_rule(
            workspace, name, request
        )
