import json
import os
from datetime import datetime
from distutils.util import strtobool

from jsonschema import SchemaError, ValidationError, validate

from app.api import ManagedServersApiClient
from app.api.utils import AutoFixStatus, Service
from app.config import API_SCHEMA_PATH, TEMP_ACKNOWLEDGE_DURATION, logger
from app.utils import ManagementType, Result, ScriptStatus, ServiceStatus, load_schema
from app.utils.check_processing.ticket_reply import (
    check_ticket_necessity,
    prepare_payload_for_ticket,
)
from app.utils.logging import AutofixLogger


def process_script_status(service: str, check_result: dict) -> Result:
    result = Result(data={"abort_execution": False, "report_error": False})
    if check_result.get("script_status"):
        status = check_result["script_status"]
        if status == ScriptStatus.STARTED.codename:
            msg = f"Started checking service '{service}'"
            logger.info(msg)
            result.data["abort_execution"] = True
        elif status == ScriptStatus.IN_PROGRESS.codename:
            msg = f"Statistics calculation for service '{service}' is still in progress"
            logger.info(msg)
            result.data["abort_execution"] = True
        elif status in [
            ScriptStatus.TIMEOUT.codename,
            ScriptStatus.CANNOT_START.codename,
        ]:
            if status == ScriptStatus.TIMEOUT.codename:
                msg = (
                    f"Statistics calculation for service '{service}' timed out after 24 hours of execution. "
                    f"Please check manually"
                )
            else:
                msg = "Failed to start service check script"
            result.data["report_error"] = True
            result.data["error"] = msg
            logger.error(msg)
        elif status == ScriptStatus.SKIP.codename:
            logger.warning(
                f"Service {service} is temporarily disabled. Skipping this check. To enable it, remove the lock file",
            )
            result.data["abort_execution"] = True
        else:
            msg = f"Unrecognized script status {status} for service '{service}' check"
            logger.error(msg)
            result.data["abort_execution"] = True
    return result


def process_auto_fix_results(service: Service, check_result: dict) -> Result:
    result = Result(
        data={"abort_execution": False, "client_notified": False, "payload": {}},
    )
    result.data.update(check_result)
    result.data["auto_fix_status"] = check_result.get("auto_fix_status")
    try:
        force_report = bool(strtobool(result.data.get("force_report", "false")))
    except ValueError:
        force_report = False
    if result.data["payload"].get("service_status") == ServiceStatus.OK.codename:
        logger.info(
            f"Check finished, service {service.codename} has no issues: {json.dumps(check_result, indent=2)}",
        )
        result.data["abort_execution"] = True
        return result

    elif result.data["payload"].get("service_status") == ServiceStatus.DISABLED.codename:
        result.data["payload"]["monitoring_comment"] = "The service has been disabled"
    elif result.data["payload"].get("service_status") == ServiceStatus.BACKUP_IN_PROGRESS.codename:
        current_timestamp = int(datetime.now().timestamp())
        expiry = current_timestamp + TEMP_ACKNOWLEDGE_DURATION
        result.data["payload"]["acknowledge_expiry_timestamp"] = expiry
        status_verbose = (
            "has been started"
            if result.data["payload"].get("service_status") == ServiceStatus.BACKUP_STARTED.codename
            else "is already in progress"
        )
        result.data["payload"]["monitoring_comment"] = f"Backup {status_verbose}: {result.data['payload']['log_file']}"
    elif result.data["payload"].get("service_status") == ServiceStatus.BACKUP_STARTED.codename:
        logger.info(
            "cPanel backup has been started, its status will be checked during the next cron run",
        )
        result.data["abort_execution"] = True
    if result.data["auto_fix_status"] == AutoFixStatus.SUCCESS.codename:
        logger.info(f"Check finished, service {service} has been fixed: {check_result}")
        if not force_report:
            result.data["abort_execution"] = True
        return result

    if force_report and result.data["auto_fix_status"] == AutoFixStatus.SUCCESS.codename:
        result.data["payload"]["monitoring_comment"] = "Auto fix has been applied successfully"
    mgmt_type = ManagementType(os.getenv("SERVER_MANAGEMENT_TYPE"))
    ticket_needed = check_ticket_necessity(mgmt_type, service, result.data)
    if ticket_needed:
        prepare_payload_for_ticket(service, result.data["payload"])
    else:
        result.data["client_notified"] = (
            False if result.data["auto_fix_status"] == AutoFixStatus.PROCESS_MANUALLY.codename else None
        )

    for var in ["recommendations", "service_data", "details"]:
        if not result.data["payload"].get(var):
            result.data["payload"][var] = ""

    return result


def process_report(
    acknowledged: bool,
    client_notified: bool,
    data: dict,
    result: Result,
    s: Service,
    auto_fix_status: str,
):
    api = ManagedServersApiClient(
        os.getenv("API_TOKEN"),
        os.getenv("API_GW_URL"),
    )
    service_status = data["payload"]["service_status"]
    log_check_result = AutofixLogger.check_logged_api_reports(
        s.codename,
        service_status,
    )
    for warn in log_check_result.warnings:
        logger.warning(warn)

    if log_check_result.success:
        if log_check_result.data["already_sent"]:
            logger.info(
                f"Report already sent for service {s.codename} ({service_status}) at {log_check_result.data['timestamp']}\nCurrent payload: {json.dumps(data, indent=2)}",
            )
            return
    else:
        logger.error(log_check_result.message)

    if s == Service.PUPPET:
        logger.warning(
            f"Reports for puppet_check are disabled. Run result: {json.dumps(data, indent=2)}",
        )
        return
    payload = dict(
        service=s.codename,
        payload=data["payload"],
        auto_fix_status=auto_fix_status,
        client_notified=client_notified,
        acknowledged=acknowledged,
    )
    try:
        validate(instance=payload, schema=load_schema(API_SCHEMA_PATH))
        api_response = api.add_service_check(**payload)

        if api_response.success:
            if api_response.warnings:
                for warn in result.warnings:
                    logger.warning(warn)
            logger.info(
                f"Report sent for service {s.codename} ({service_status})\n{json.dumps(data, indent=2)}",
            )
        else:
            logger.error(
                f"Failed to add service check for {s.codename}\n{api_response.message}",
            )
    except SchemaError as err:
        error_msg = err.message or repr(err)
        logger.error(
            f"Failed to add service check for {s.codename}\n{error_msg}",
        )
    except ValidationError as err:
        logger.error(
            (
                f"Failed to add service check for {s.codename} - API payload structure is invalid: {err.message} in\n{json.dumps(payload, indent=2)}\n"
                if err.message
                else repr(err)
            ),
        )
