from typing import Optional

import attr
from staff.lib import waffle
from django.http import JsonResponse

from django.db.models import Count, Sum
from django.utils.translation import get_language
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_http_methods

from staff.budget_position.models import BudgetPositionAssignment
from staff.departments.models import Department, RelevanceDate, HeadcountPosition
from staff.departments.tree_lib import LeafPager
from staff.lib.decorators import responding_json, use_request_lang, available_by_tvm
from staff.lib.utils.qs_values import localize
from staff.person.models import Staff

from staff.headcounts.budget_position_assignment_entity_info import BudgetPositionAssignmentEntityInfo
from staff.headcounts.budget_position_assignment_filter_context import BudgetPositionAssignmentFilterContext
from staff.headcounts.forms import HeadcountsFilterForm, OldHeadcountsFilterForm
from staff.headcounts.headcounts_summary_model import HeadcountsSummaryModel
from staff.headcounts.permissions import Permissions
from staff.headcounts.positions_entity_info import PositionsEntityInfo
from staff.headcounts.positions_filter_context import PositionsFilterContext


@ensure_csrf_cookie
@responding_json
@require_http_methods(['GET'])
@use_request_lang
def old_ceilings(request, url=None):
    skip = int(request.GET.get('skip', 0))
    permissions = Permissions(request.user.get_profile())

    try:
        if url and not permissions.has_access_to_department_url(url):
            return {'errors': ['permission_denied']}, 403

        filter_form = OldHeadcountsFilterForm.from_query_dict(request.GET)

        if not filter_form.is_valid():
            return filter_form.errors_as_dict(), 400

        filter_context = PositionsFilterContext(
            observer_permissions=permissions,
            **filter_form.cleaned_data
        )

        filler = PositionsEntityInfo(filter_context)

        pager = LeafPager(filler, url, skip=skip)
        departments, continuation_token = pager.get_grouped_entities()
    except Department.DoesNotExist:
        return {'errors': ['not_found']}, 404

    result = {
        'departments': departments,
        'can_manage_credit': waffle.switch_is_active('enable_credit_management'),
    }

    if continuation_token:
        result['next_skip'] = continuation_token

    if skip == 0:
        dates = RelevanceDate.objects.first()
        result['positions_count'] = 0
        result['hc_sum'] = 0
        result['relevance_date'] = dates.relevance_date
        result['last_sync_time'] = dates.last_sync_time

        count_hc_sum = (
            pager
            .get_aggregate_qs()
            .aggregate(positions_count=Count('id'), hc_sum=Sum('headcount'))
        )
        if count_hc_sum['positions_count']:
            result.update(count_hc_sum)

        if pager.department:
            result['url_department_name'] = localize(pager.department)['name']

    for dep_param in ('department', 'value_stream', 'geography'):
        dep_obj: Optional[Department] = filter_form.cleaned_data.get(dep_param)
        if dep_obj:
            result[dep_param] = {
                'id': dep_obj.id,
                'url': dep_obj.url,
                'name': dep_obj.name,
            }

    for user_param in ('replaced_person', 'current_person'):
        user_obj: Optional[Staff] = filter_form.cleaned_data.get(user_param)
        if user_obj:
            result[user_param] = {
                'login': user_obj.login,
                'name': user_obj.get_full_name(),
            }

    return result


@ensure_csrf_cookie
@responding_json
@require_http_methods(['GET'])
@use_request_lang
def budget_position_assignments(request, url=None):
    skip = int(request.GET.get('skip', 0))
    permissions = Permissions(request.user.get_profile())

    try:
        if url and not permissions.has_access_to_department_url(url):
            return {'errors': ['permission_denied']}, 403

        filter_form = HeadcountsFilterForm.from_query_dict(request.GET)

        if not filter_form.is_valid():
            return filter_form.errors_as_dict(), 400

        filter_context = BudgetPositionAssignmentFilterContext(
            observer_permissions=permissions,
            **filter_form.cleaned_data
        )

        filler = BudgetPositionAssignmentEntityInfo(filter_context)

        pager = LeafPager(filler, url, skip=skip)
        departments, continuation_token = pager.get_grouped_entities()
    except Department.DoesNotExist:
        return {'errors': ['not_found']}, 404

    result = {
        'departments': departments,
        'can_manage_credit': waffle.switch_is_active('enable_credit_management'),
    }

    if continuation_token:
        result['next_skip'] = continuation_token

    if skip == 0:
        dates = RelevanceDate.objects.get(model_name=BudgetPositionAssignment.__name__)
        result['positions_count'] = 0
        result['hc_sum'] = 0
        result['relevance_date'] = dates.relevance_date
        result['last_sync_time'] = dates.last_sync_time
        result['last_sync_result'] = {
            'updated_entities': dates.updated_entities,
            'failed_entities': dates.failed_entities,
            'skipped_entities': dates.skipped_entities,
        }

        count_hc_sum = (
            pager
            .get_aggregate_qs()
            .aggregate(positions_count=Count('id'), hc_sum=Sum('budget_position__headcount'))
        )
        if count_hc_sum['positions_count']:
            result.update(count_hc_sum)

        if pager.department:
            result['url_department_name'] = localize(pager.department)['name']

    for dep_param in ('department', 'value_stream', 'geography'):
        dep_obj: Optional[Department] = filter_form.cleaned_data.get(dep_param)
        if dep_obj:
            result[dep_param] = {
                'id': dep_obj.id,
                'url': dep_obj.url,
                'name': dep_obj.name,
            }

    for user_param in ('replaced_person', 'current_person'):
        user_obj: Optional[Staff] = filter_form.cleaned_data.get(user_param)
        if user_obj:
            result[user_param] = {
                'login': user_obj.login,
                'name': user_obj.get_full_name(),
            }

    return result


@responding_json
@require_http_methods(['GET'])
@use_request_lang
def headcounts_summary(request, login):
    permissions = Permissions(request.user.get_profile())
    try:
        observable = Staff.objects.get(login=login)
        if not permissions.has_access_to_person(observable):
            return {'errors': ['permission_denied']}, 403
    except Staff.DoesNotExist:
        return {'errors': ['not_found']}, 404

    model = HeadcountsSummaryModel(request.user.get_profile(), True, get_language())
    vs_result = attr.asdict(model.departments_summary_for_person(observable, valuestream_mode=True))
    result = attr.asdict(model.departments_summary_for_person(observable, valuestream_mode=False))
    result['value_streams'] = vs_result['departments']
    result['login'] = observable.login
    result['first_name'] = observable.first_name
    result['last_name'] = observable.last_name

    return result


@responding_json
@require_http_methods(['GET'])
@use_request_lang
def headcounts_summary_for_department(request, url):
    show_nested = request.GET.get('show_nested', False)
    permissions = Permissions(request.user.get_profile())
    try:
        if not permissions.has_access_to_department_url(url):
            return {'errors': ['permission_denied']}, 403

    except Department.DoesNotExist:
        return {'errors': ['not_found']}, 404

    model = HeadcountsSummaryModel(request.user.get_profile(), False, get_language())
    result_department = model.departments_summary_for_department(url, show_nested)

    def without_none_chief(attribute, value):
        if attribute.name == 'chief' and not value:
            return False

        return True

    return {
        'department': attr.asdict(result_department, filter=without_none_chief),
    }


PAGE_SIZE = 5000


@available_by_tvm(['yt'])
@require_http_methods(['GET'])
def login_to_vs(request):
    continuation_token = request.GET.get('continuation_token', None)
    qs = HeadcountPosition.objects.filter(current_login__isnull=False, main_assignment=True)

    fields = (
        'current_login',
        'valuestream__url',
        'valuestream__name',
        'hr_product_id',
        'hr_product__service_id',
    )

    if continuation_token:
        qs = qs.filter(current_login__gt=continuation_token)

    db_data = list(qs.order_by('current_login').values(*fields).distinct()[:PAGE_SIZE])
    data = {
        hp['current_login']: {
            'vs_url': hp['valuestream__url'],
            'vs_name': hp['valuestream__name'],
            'hr_product_id': hp['hr_product_id'],
            'service_id': hp['hr_product__service_id'],
        }
        for hp in db_data
    }
    result = {'data': data}

    if len(db_data) == PAGE_SIZE:
        result['continuation_token'] = db_data[-1]['current_login']

    return JsonResponse(data=result)
