# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Pre-build views for simplifying reads from DB."""
from __future__ import annotations

from dataclasses import asdict
from typing import Any

from googleads_housekeeper.services import unit_of_work

registry = {}


def task(task_id: int,
         uow: unit_of_work.AbstractUnitOfWork) -> dict[str, Any] | None:
    with uow:
        if uow_task := uow.tasks.get(task_id):
            return asdict(uow_task)
        return None


def tasks(uow: unit_of_work.AbstractUnitOfWork) -> list[dict[str, Any]]:
    with uow:
        uow_tasks: list[dict[str, Any]] = []
        for uow_task in uow.tasks.list():
            task_dict = uow_task.to_dict()
            task_dict['next_run'] = uow_task.next_run
            accounts = customer_ids(
                uow=uow, accounts=uow_task.customer_ids.split(','))
            task_dict['accounts'] = list(
                {a.get('account_name') for a in accounts})
            if task_executions := executions(uow_task.id, uow):
                for current_execution in sorted(
                        task_executions,
                        key=lambda x: x.get('end_time'),
                        reverse=True):
                    task_dict['placements_excluded'] = current_execution.get(
                        'placements_excluded')
                    task_dict['last_run'] = current_execution.get(
                        'end_time').strftime('%Y-%m-%d %H-%M')
                    break
            else:
                task_dict['placements_excluded'] = None
            uow_tasks.append(task_dict)
        return uow_tasks


def config(uow: unit_of_work.AbstractUnitOfWork) -> list[dict[str, Any]]:
    with uow:
        return [asdict(c) for c in uow.settings.list()]


def allowlisted_placements(uow: unit_of_work.AbstractUnitOfWork,
                           account_id: str | None = None) -> list[dict] | None:
    with uow:
        if account_id:
            if uow_allowlisted_placements := uow.allowlisting.get_by_conditions(
                {'account_id': account_id}):
                return [asdict(t) for t in uow_allowlisted_placements]
        if uow_allowlisted_placements := uow.allowlisting.list():
            return [asdict(t) for t in uow_allowlisted_placements]
    return None


def allowlisted_placements_as_tuples(
        uow: unit_of_work.AbstractUnitOfWork,
        account_id: str | None = None) -> list[tuple[str, str, str]]:
    if placements := allowlisted_placements(uow, account_id):
        return [(p.get('account_id'), p.get('type'),
                 p.get('name')) for p in placements]
    return []


def execution(execution_id: str,
              uow: unit_of_work.AbstractUnitOfWork) -> dict[str, Any] | None:
    with uow:
        if task_execution := uow.executions.get(execution_id):
            return asdict(task_execution)
    return None


def executions(
        task_id: str,
        uow: unit_of_work.AbstractUnitOfWork) -> list[dict[str, Any]] | None:
    with uow:
        if task_executions := uow.executions.get_by_condition('task', task_id):
            return [asdict(t) for t in task_executions]
    return None


def execution_details(
        task_id: str,
        execution_id: str,
        uow: unit_of_work.AbstractUnitOfWork,
        first_n_placements: int | None = None) -> list[dict[str, Any]] | None:
    with uow:
        if uow.executions.get_by_condition('task', task_id):
            if uow_execution_details := uow.execution_details.get_by_condition(
                    'execution_id', execution_id):
                results = [asdict(t) for t in uow_execution_details]
                if first_n_placements and first_n_placements < len(results):
                    results = results[:first_n_placements]
                return results
    return None


def customer_ids(uow: unit_of_work.AbstractUnitOfWork,
                 mcc_id: str | None = None,
                 accounts: list[str] | None = None) -> list[dict[str, Any]]:
    with uow:
        if mcc_id:
            uow_customer_ids = [
                asdict(r) for r in uow.customer_ids.list() if r.mcc_id == mcc_id
            ]
        elif accounts:
            uow_customer_ids = [
                asdict(r) for r in uow.customer_ids.list() if r.id in accounts
            ]
        else:
            raise ValueError('Neither mcc_id nor accounts were provided')
        return uow_customer_ids


def mcc_ids(uow: unit_of_work.AbstractUnitOfWork) -> list[dict[str, str]]:
    if registry_mcc_ids := registry.get('mcc_ids'):
        return registry_mcc_ids
    with uow:
        uow_mcc_ids = [asdict(r) for r in uow.mcc_ids.list()]
        registry['mcc_ids'] = uow_mcc_ids
        return uow_mcc_ids
