import json
import logging
import uuid
from dataclasses import dataclass
from datetime import datetime, timedelta
from json import JSONDecodeError
from typing import Dict, Optional, List
from urllib.parse import urljoin

import requests
from django.contrib.auth.models import User
from django.db.utils import IntegrityError

from company.models.company import Company
from console.settings.base import NCIRCC_DOMAIN_NAME, NCIRCC_CERT_VERIFY
from ncircc.models.comments import Comment
from ncircc.models.notification import Notification
from ncircc.serializers.comments import CommentRetrieveSerializer

_log = logging.getLogger(__name__)


@dataclass
class CommentSuccessDataResponse:
    """Data for success comment created"""
    id: int
    incident_id: int
    comment: str
    user_id: int
    d_comment: datetime
    t_comment: str
    createdAt: datetime
    updatedAt: datetime
    to: str
    parent_id: Optional[int]
    workgroup_resolved: bool
    options: dict


@dataclass
class CommentGettingResponse:
        id: int
        incident_uuid: uuid.UUID
        text: str
        login: str
        incident_id: str
        create_time: datetime


class CommentSenderService:
    """Send comment to notification."""
    path = '/api/v2/comments'

    def __init__(self, message: str, notification_pk: int, user: User) -> None:
        self.user = user
        self.company = Company.objects.first()
        self.message = message
        notification = Notification.objects.get(pk=notification_pk)
        self.notification_uuid = str(notification.uuid)

    def get_headers(self) -> Dict[str, str]:
        """Return headers for sending."""
        headers = {
            'x-token': self.company.api_key,
            'Content-Type': 'application/json',
            }
        return headers

    def get_payload(self) -> dict:
        data = {
            'incident.uuid': self.notification_uuid,
            'data': {'comment': self.message}
        }
        return data

    def _save(self, data: CommentSuccessDataResponse) -> Comment:
        comment = Comment(
            text=self.message,
            notification=Notification.objects.get(uuid=self.notification_uuid),
            id_in_ncircc=data.id,
            create_time=data.createdAt,
            login=str(self.user.username),
            is_read=True,
            from_console=True,
        )
        comment.save()
        return CommentRetrieveSerializer(comment).data

    def send(self):
        url = urljoin(NCIRCC_DOMAIN_NAME, self.path)
        msg, status = 'Success', 200
        headers = self.get_headers()
        payload = self.get_payload()

        try:
            response = requests.post(url, json=payload, headers=headers, verify=NCIRCC_CERT_VERIFY)
        except requests.exceptions.RequestException as err:
            _log.error(f'Error: {err}')
            return str(err), 400
        try:
            data = response.json()
        except (JSONDecodeError, AttributeError) as err:
            _log.error(f'Error: {err}')
            return str(err), 400
        if response.status_code == 200:
            data = self._save(CommentSuccessDataResponse(**data.get('data', {})))
            return data, status
        else:
            return 'Error', 400


class CommentsUpdaterService:
    """Service for getting all comments and save it in db"""

    update_delta = timedelta(minutes=10)  # todo после уточнения изменить
    company = None
    path = 'api/v2/incidents/comments'

    def __init__(self) -> None:
        self.company = Company.objects.first()

    def get_headers(self) -> Dict[str, str]:
        """Return headers for sending."""
        headers = {
            'x-token': self.company.api_key,
            'Content-Type': 'application/json',
        }
        return headers

    def get_payload(self) -> tuple:
        data_value = datetime.now() - self.update_delta
        params = [
            {"property": "create_time", "operator": "gte", "value": str(data_value.isoformat())},
            {"property": "login", "operator": "endsWith", "value": "НКЦКИ"}]
        return ('filter', json.dumps(params),),

    def send(self):
        _log.debug('start1')
        url = urljoin(NCIRCC_DOMAIN_NAME, self.path)
        payload = self.get_payload()
        headers = self.get_headers()
        try:
            response = requests.get(url, headers=headers, params=payload, verify=NCIRCC_CERT_VERIFY)
        except requests.exceptions.RequestException as err:
            _log.error(f'Error: {err}')
            return 'Error', 400
        try:
            data = response.json()
        except (JSONDecodeError, AttributeError) as err:
            _log.error(f'Error: {err}')
            return 'Error', 400

        if response.status_code == 200:
            data = data.get('data', [])
            comment_list = [CommentGettingResponse(**self.clear_comments_request(comment)) for comment in data]
            self._save(comment_list)
            return 'Success', 200
        else:
            _log.error(f'Error: {data}')
            return 'Error', 400

    def clear_comments_request(self, data: dict) -> dict:
        """Clear json from ncircc."""
        if 'incident.uuid' in data:
            data['incident_uuid'] = data.pop('incident.uuid')
        if 'incident.id' in data:
            data['incident_id'] = data.pop('incident.id')
        return data

    def _save(self, comments: List[CommentGettingResponse]) -> None:
        """bulk create comments in db"""
        incidents_uuid = [comment.incident_uuid for comment in comments]
        _log.debug(f'start bulk save: {incidents_uuid}')
        notifications = Notification.objects.filter(uuid__in=incidents_uuid)
        notifications_map = {str(notification.uuid): notification for notification in notifications}

        for comment in comments:
            if notifications_map.get(comment.incident_uuid):
                try:
                    Comment.objects.create(text=comment.text, notification=notifications_map[comment.incident_uuid],
                                           create_time=comment.create_time, login=comment.login,
                                           id_in_ncircc=comment.id)
                except IntegrityError as err:
                    _log.debug(f'Cannot save comment {comment.id}: {err}')
        _log.debug(f'Saved: {incidents_uuid}')
