import itertools
import logging
from typing import Any, Dict, Iterator, List, Set

from django.db.transaction import get_connection

from staff.lib.bulk_upsert import make_upsert_query
from staff.lib.db import atomic

from staff.syncs.models import StartrekVs, StartrekUmbrella
from staff.syncs.umbrellas.umbrellas_fetcher import UmbrellasFetcher, VS


logger = logging.getLogger(__name__)


class UmbrellasSync:
    def __init__(self, fetcher: UmbrellasFetcher):
        self.fetcher = fetcher
        self.batch_size = 10
        self.processed_vs_issue_keys: List[str] = []
        self.processed_umbrella_issue_keys: List[str] = []

    def _iterate_by_batches(self, iterator: Iterator[VS]) -> Iterator[List[VS]]:
        result = list(itertools.islice(iterator, self.batch_size))
        while result:
            yield result
            result = list(itertools.islice(iterator, self.batch_size))

    def sync(self) -> None:
        for batch in self._iterate_by_batches(self.fetcher.get_umbrellas_from_st()):
            with atomic():
                connection = get_connection()
                self._update_vs(batch, connection)
                self._update_umbrellas(batch, connection)

        self._delete_missing()

    def _get_already_existing_keys(self, objects: List[StartrekVs]) -> Set[str]:
        return {obj.issue_key for obj in objects}

    def _update_vs(self, batch: List[VS], connection: Any) -> None:
        if not batch:
            return
        vs_issue_keys = {vs.issue_key for vs in batch}
        self._bulk_upsert_vs(batch, connection)
        self.processed_vs_issue_keys += vs_issue_keys

    def _update_umbrellas(self, batch: List[VS], connection: Any) -> None:
        umbrella_issue_keys = {umbrella.issue_key for vs in batch for umbrella in vs.umbrellas}
        if not umbrella_issue_keys:
            return
        vs_issue_key_to_vs_id = dict(StartrekVs.objects.values_list('issue_key', 'pk'))
        self._bulk_upsert_umbrellas(batch, vs_issue_key_to_vs_id, connection)
        self.processed_umbrella_issue_keys += umbrella_issue_keys

    def _bulk_upsert_vs(self, batch: List[VS], connection: Any) -> None:
        fields = ['issue_key', 'abc_service_id', 'name', 'deadline']
        values = [
            [getattr(vs, field) for field in fields]
            for vs in batch
        ]

        query = make_upsert_query(StartrekVs._meta.db_table, fields, 'issue_key', len(values))

        cursor = connection.cursor()
        params = [value for row in values for value in row]
        cursor.execute(query, params)

    def _bulk_upsert_umbrellas(self, batch: List[VS], vs_issue_key_to_vs_id: Dict[str, int], connection: Any) -> None:
        fields = ['issue_key', 'vs_id', 'name']
        values = [
            [umbrella.issue_key, vs_issue_key_to_vs_id[vs.issue_key], umbrella.name]
            for vs in batch
            for umbrella in vs.umbrellas
        ]

        query = make_upsert_query(StartrekUmbrella._meta.db_table, fields, 'issue_key', len(values))

        cursor = connection.cursor()
        params = [value for row in values for value in row]
        cursor.execute(query, params)

    def _delete_missing(self) -> None:
        StartrekVs.objects.exclude(issue_key__in=self.processed_vs_issue_keys).delete()
        StartrekUmbrella.objects.exclude(issue_key__in=self.processed_umbrella_issue_keys).delete()
