<?php

namespace MintyPHP\Repository\Org;

use MintyPHP\DB;
use MintyPHP\Repository\Support\RepoQuery;
use MintyPHP\Repository\Support\RepositoryArrayHelper;

class DepartmentRepository implements DepartmentRepositoryInterface
{
    public function list(): array
    {
        $rows = DB::select(
            'select id, uuid, description, tenant_id, code, cost_center, active, created_by, modified_by, created, modified from departments order by id desc'
        );
        return RepositoryArrayHelper::unwrapList($rows, 'departments');
    }

    public function listIds(): array
    {
        $rows = DB::select('select id from departments');

        return RepositoryArrayHelper::extractIds($rows, 'departments');
    }

    public function listActiveIdsByTenantIds(array $tenantIds): array
    {
        $tenantIds = RepositoryArrayHelper::sanitizePositiveIds($tenantIds);
        if (!$tenantIds) {
            return [];
        }
        $placeholders = implode(',', array_fill(0, count($tenantIds), '?'));
        $rows = DB::select(
            'select id from departments where active = 1 and tenant_id in (' . $placeholders . ')',
            ...array_map('strval', $tenantIds)
        );

        return RepositoryArrayHelper::extractIds($rows, 'departments');
    }

    public function listByTenantIds(array $tenantIds): array
    {
        $tenantIds = RepositoryArrayHelper::sanitizePositiveIds($tenantIds);
        if (!$tenantIds) {
            return [];
        }
        $placeholders = implode(',', array_fill(0, count($tenantIds), '?'));
        $rows = DB::select(
            'select departments.id, departments.uuid, departments.description, departments.tenant_id, departments.code, departments.cost_center, departments.active, departments.created_by, departments.modified_by, departments.created, departments.modified ' .
            "from departments departments join tenants t on t.id = departments.tenant_id and t.status = 'active' " .
            'where departments.active = 1 and departments.tenant_id in (' . $placeholders . ') ' .
            'order by departments.description asc',
            ...array_map('strval', $tenantIds)
        );
        return RepositoryArrayHelper::unwrapList($rows, 'departments');
    }

    public function listByIds(array $departmentIds, bool $includeInactive = false): array
    {
        $departmentIds = RepositoryArrayHelper::sanitizePositiveIds($departmentIds);
        if (!$departmentIds) {
            return [];
        }
        $placeholders = implode(',', array_fill(0, count($departmentIds), '?'));
        $activeSql = $includeInactive ? '' : 'active = 1 and ';
        $rows = DB::select(
            'select id, uuid, description, tenant_id, code, cost_center, active, created_by, modified_by, created, modified from departments where ' . $activeSql . 'id in (' . $placeholders . ') order by description asc',
            ...array_map('strval', $departmentIds)
        );
        return RepositoryArrayHelper::unwrapList($rows, 'departments');
    }

    public function listPaged(array $options): array
    {
        $search = trim((string) ($options['search'] ?? ''));
        $tenant = trim((string) ($options['tenant'] ?? ''));
        $allowedOrder = ['id', 'uuid', 'description', 'code', 'cost_center', 'active', 'created', 'modified'];
        [$limit, $offset] = RepoQuery::sanitizeLimitOffset($options);
        [$order, $dir] = RepoQuery::sanitizeOrder($options, $allowedOrder);

        $where = [];
        $params = [];
        RepoQuery::addLikeFilter(
            $where,
            $params,
            ['description', 'uuid', 'code', 'cost_center'],
            $search
        );
        $activeValue = array_key_exists('active', $options) ? $options['active'] : null;
        RepoQuery::addEnumFilter($where, $params, $activeValue, [
            ['aliases' => ['1', 'true', 'active'], 'sql' => 'departments.active = 1'],
            ['aliases' => ['0', 'false', 'inactive'], 'sql' => 'departments.active = 0'],
        ]);
        RepoQuery::addEqualsFilter(
            $where,
            $params,
            $tenant,
            "exists (select 1 from tenants t where t.id = departments.tenant_id and t.status = 'active' and t.uuid = ?)"
        );

        if (!empty($options['tenantUserId'])) {
            $tenantUserId = (int) $options['tenantUserId'];
            if ($tenantUserId > 0) {
                $where[] = 'exists (select 1 from user_tenants ut ' .
                    "join tenants t on t.id = ut.tenant_id and t.status = 'active' " .
                    'where ut.user_id = ? and ut.tenant_id = departments.tenant_id)';
                $params[] = (string) $tenantUserId;
            }
        } elseif (array_key_exists('tenantIds', $options)) {
            $tenantIds = $options['tenantIds'];
            $tenantIds = is_array($tenantIds) ? array_values(array_unique(array_map('intval', $tenantIds))) : [];
            if ($tenantIds) {
                $where[] = 'departments.tenant_id in (???)';
                $params[] = $tenantIds;
            } else {
                $where[] = '1=0';
            }
        }

        $whereSql = $where ? (' where ' . implode(' and ', $where)) : '';
        $count = DB::selectValue('select count(*) from departments' . $whereSql, ...$params);
        $total = $count ? (int) $count : 0;

        $query = 'select id, uuid, description, tenant_id, code, cost_center, active, created_by, modified_by, created, modified from departments' .
            $whereSql .
            sprintf(' order by `%s` %s limit ? offset ?', $order, $dir);

        $queryParams = array_merge($params, [(string) $limit, (string) $offset]);
        $rows = call_user_func_array(['MintyPHP\\DB', 'select'], array_merge([$query], $queryParams));

        $list = RepositoryArrayHelper::unwrapList($rows, 'departments');
        $ids = [];
        foreach ($list as $department) {
            if (isset($department['id'])) {
                $ids[] = (int) $department['id'];
            }
        }
        $tenantLabelsByDepartment = [];
        if ($ids) {
            $placeholders = implode(',', array_fill(0, count($ids), '?'));
            $labelRows = DB::select(
                'select d.id as department_id, t.description as description from departments d join tenants t on t.id = d.tenant_id ' .
                'where d.id in (' . $placeholders . ') order by t.description asc',
                ...array_map('strval', $ids)
            );
            foreach ((array) $labelRows as $row) {
                $departmentId = 0;
                $label = '';
                foreach ((array) $row as $table) {
                    if (!is_array($table)) {
                        continue;
                    }
                    if ($departmentId === 0 && isset($table['department_id'])) {
                        $departmentId = (int) $table['department_id'];
                    }
                    if ($label === '' && isset($table['description'])) {
                        $label = (string) $table['description'];
                    }
                }
                if ($departmentId > 0 && $label !== '') {
                    $tenantLabelsByDepartment[$departmentId] = [$label];
                }
            }
        }
        foreach ($list as &$department) {
            $departmentId = (int) ($department['id'] ?? 0);
            $department['tenant_labels'] = $tenantLabelsByDepartment[$departmentId] ?? [];
        }
        unset($department);

        return [
            'total' => $total,
            'rows' => $list,
        ];
    }

    public function find(int $id): ?array
    {
        $row = DB::selectOne(
            'select id, uuid, description, tenant_id, code, cost_center, active, created_by, modified_by, created, modified from departments where id = ? limit 1',
            (string) $id
        );
        return RepositoryArrayHelper::unwrap($row, 'departments');
    }

    public function findByUuid(string $uuid): ?array
    {
        $row = DB::selectOne(
            'select id, uuid, description, tenant_id, code, cost_center, active, created_by, modified_by, created, modified from departments where uuid = ? limit 1',
            $uuid
        );
        return RepositoryArrayHelper::unwrap($row, 'departments');
    }

    public function existsByCode(string $code, int $excludeId = 0): bool
    {
        $code = trim($code);
        if ($code === '') {
            return false;
        }
        if ($excludeId > 0) {
            $count = DB::selectValue('select count(*) from departments where code = ? and id != ?', $code, (string) $excludeId);
        } else {
            $count = DB::selectValue('select count(*) from departments where code = ?', $code);
        }
        return (int) $count > 0;
    }

    public function existsByTenantAndDescription(int $tenantId, string $description, int $excludeId = 0): bool
    {
        $tenantId = (int) $tenantId;
        $description = trim($description);
        if ($tenantId <= 0 || $description === '') {
            return false;
        }

        if ($excludeId > 0) {
            $count = DB::selectValue(
                'select count(*) from departments where tenant_id = ? and lower(description) = lower(?) and id != ?',
                (string) $tenantId,
                $description,
                (string) $excludeId
            );
        } else {
            $count = DB::selectValue(
                'select count(*) from departments where tenant_id = ? and lower(description) = lower(?)',
                (string) $tenantId,
                $description
            );
        }

        return (int) $count > 0;
    }

    public function countByTenantId(int $tenantId): int
    {
        if ($tenantId <= 0) {
            return 0;
        }
        $count = DB::selectValue('select count(*) from departments where tenant_id = ?', (string) $tenantId);
        return $count ? (int) $count : 0;
    }

    public function create(array $data): mixed
    {
        return DB::insert(
            'insert into departments (uuid, description, tenant_id, code, cost_center, active, created_by, created) values (?,?,?,?,?,?,?,NOW())',
            $data['uuid'] ?? RepoQuery::uuidV4(),
            $data['description'],
            (string) ($data['tenant_id'] ?? 0),
            $data['code'] ?? null,
            $data['cost_center'] ?? null,
            $data['active'] ?? 1,
            $data['created_by'] ?? null
        );
    }

    public function update(int $id, array $data): bool
    {
        $fields = [
            'description' => $data['description'],
            'tenant_id' => (string) ($data['tenant_id'] ?? 0),
            'code' => $data['code'] ?? null,
            'cost_center' => $data['cost_center'] ?? null,
            'active' => $data['active'] ?? 1,
        ];
        if (array_key_exists('modified_by', $data)) {
            $fields['modified_by'] = $data['modified_by'];
        }

        $setParts = [];
        $params = [];
        foreach ($fields as $field => $value) {
            $setParts[] = sprintf('`%s` = ?', $field);
            $params[] = $value;
        }
        $params[] = (string) $id;

        $query = 'update departments set ' . implode(', ', $setParts) . ' where id = ?';
        $result = DB::update($query, ...$params);
        return $result !== false;
    }

    public function setTenant(int $id, int $tenantId): bool
    {
        if ($id <= 0 || $tenantId <= 0) {
            return false;
        }
        $result = DB::update(
            'update departments set tenant_id = ? where id = ?',
            (string) $tenantId,
            (string) $id
        );
        return $result !== false;
    }

    public function delete(int $id): bool
    {
        $result = DB::delete('delete from departments where id = ?', (string) $id);
        return $result !== false;
    }
}
