/** پنل مرکزی مدیریت: کاربران، نقش‌ها، تأییدها، قواعد مسیریابی، حسابرسی، تحلیل، سلامت سیستم */
import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { and, desc, eq, sql } from 'drizzle-orm';
import { getDb } from '../../database/client.js';
import {
  users, roles, permissions, userRoles, organizations, businesses, routingRules, auditLogs,
  analyticsEvents, taxonomies, settings, mediaAssets, searchQueries, cases, reports, articles, datasets,
} from '../../database/schema/index.js';
import { requireAuth } from '../../guards/index.js';
import { assignRole, can } from '../../modules/rbac/service.js';
import { recordAudit, AuditActions } from '../../modules/audit/service.js';
import { reindexAll } from '../../modules/search/indexer.js';
import { runSlaSweep } from '../../modules/reports/service.js';
import { runEditorialMaintenance } from '../../modules/articles/service.js';
import { Errors } from '../../common/errors.js';
import { indexEntity } from '../../modules/search/indexer.js';

function guard(req: any, permission: string) {
  const principal = requireAuth(req);
  if (!can(principal, permission)) throw Errors.forbidden();
  return principal;
}

export default async function adminRoutes(app: FastifyInstance) {
  /* -------------------------------- خلاصه ---------------------------------- */
  app.get('/overview', async (req) => {
    guard(req, 'analytics.view');
    const db = getDb();
    const counts = await db.execute<Record<string, number>>(sql`
      SELECT
        (SELECT COUNT(*) FROM users)::int AS users,
        (SELECT COUNT(*) FROM organizations)::int AS organizations,
        (SELECT COUNT(*) FROM businesses)::int AS businesses,
        (SELECT COUNT(*) FROM articles WHERE status = 'published')::int AS published_articles,
        (SELECT COUNT(*) FROM articles WHERE status IN ('submitted','in_review'))::int AS pending_articles,
        (SELECT COUNT(*) FROM reports WHERE status IN ('submitted','moderation'))::int AS pending_reports,
        (SELECT COUNT(*) FROM cases WHERE status NOT IN ('resolved','closed'))::int AS open_cases,
        (SELECT COUNT(*) FROM cases WHERE sla_breached_at IS NOT NULL)::int AS overdue_cases,
        (SELECT COUNT(*) FROM datasets WHERE status = 'published')::int AS datasets
    `);
    const failedSearches = await db.select({ q: searchQueries.rawQuery, c: sql<number>`count(*)::int` })
      .from(searchQueries).where(eq(searchQueries.resultCount, 0)).groupBy(searchQueries.rawQuery)
      .orderBy(desc(sql`count(*)`)).limit(10);
    const recentAudit = await db.select().from(auditLogs).orderBy(desc(auditLogs.createdAt)).limit(20);
    return { counts: counts.rows?.[0] ?? {}, failedSearches, recentAudit };
  });

  /* -------------------------------- کاربران -------------------------------- */
  app.get('/users', async (req) => {
    guard(req, 'user.manage');
    const rows = await getDb().execute<{ id: string; email: string; display_name: string; status: string; roles: string[] }>(sql`
      SELECT u.id, u.email, u.display_name, u.status,
             COALESCE(array_agg(r.key) FILTER (WHERE r.key IS NOT NULL), '{}') AS roles
      FROM users u
      LEFT JOIN user_roles ur ON ur.user_id = u.id
      LEFT JOIN roles r ON r.id = ur.role_id
      GROUP BY u.id ORDER BY u.created_at DESC LIMIT 200`);
    return { items: rows.rows ?? [] };
  });

  app.post('/users/:id/roles', async (req) => {
    const principal = guard(req, 'role.manage');
    const { id } = req.params as { id: string };
    const body = z.object({
      roleKey: z.string(),
      scopeType: z.enum(['global', 'organization', 'department', 'business']).optional(),
      scopeId: z.string().uuid().optional(),
    }).parse(req.body);
    await assignRole(id, body.roleKey, { type: body.scopeType ?? 'global', id: body.scopeId ?? null }, principal.userId);
    await recordAudit({
      action: AuditActions.ROLE_CHANGE, actorId: principal.userId, entityType: 'user', entityId: id,
      after: { roleKey: body.roleKey, scopeType: body.scopeType ?? 'global', scopeId: body.scopeId ?? null },
    });
    return { ok: true };
  });

  app.delete('/users/:id/roles/:userRoleId', async (req) => {
    const principal = guard(req, 'role.manage');
    const { userRoleId, id } = req.params as { userRoleId: string; id: string };
    await getDb().delete(userRoles).where(eq(userRoles.id, userRoleId));
    await recordAudit({ action: AuditActions.ROLE_CHANGE, actorId: principal.userId, entityType: 'user', entityId: id, before: { userRoleId } });
    return { ok: true };
  });

  app.get('/roles', async (req) => {
    guard(req, 'role.manage');
    const db = getDb();
    return {
      roles: await db.select().from(roles),
      permissions: await db.select().from(permissions),
    };
  });

  /* ------------------------- تأیید سازمان و کسب‌وکار ------------------------ */
  app.post('/organizations/:id/verify', async (req) => {
    const principal = guard(req, 'organization.verify');
    const { id } = req.params as { id: string };
    const body = z.object({ status: z.enum(['verified', 'unverified', 'rejected']), note: z.string().optional() }).parse(req.body);
    const [row] = await getDb().update(organizations).set({
      verificationStatus: body.status,
      verifiedAt: body.status === 'verified' ? new Date() : null,
      verifiedBy: body.status === 'verified' ? principal.userId : null,
      updatedAt: new Date(),
    }).where(eq(organizations.id, id)).returning();
    await recordAudit({ action: AuditActions.ORG_VERIFY, actorId: principal.userId, entityType: 'organization', entityId: id, after: { status: body.status }, metadata: { note: body.note } });
    await indexEntity('organization', id);
    return row;
  });

  app.post('/businesses/:id/verify', async (req) => {
    const principal = guard(req, 'business.verify');
    const { id } = req.params as { id: string };
    const body = z.object({
      status: z.enum(['verified', 'unverified', 'rejected']),
      locationVerified: z.boolean().optional(), identityVerified: z.boolean().optional(), note: z.string().optional(),
    }).parse(req.body);
    const [row] = await getDb().update(businesses).set({
      verificationStatus: body.status,
      locationVerified: body.locationVerified ?? false,
      identityVerified: body.identityVerified ?? false,
      updatedAt: new Date(),
    }).where(eq(businesses.id, id)).returning();
    await recordAudit({ action: AuditActions.BUSINESS_VERIFY, actorId: principal.userId, entityType: 'business', entityId: id, after: { status: body.status }, metadata: { note: body.note } });
    await indexEntity('business', id);
    return row;
  });

  /* ---------------------------- قواعد مسیریابی ------------------------------ */
  app.get('/routing-rules', async (req) => {
    guard(req, 'routing.manage');
    return { items: await getDb().select().from(routingRules).orderBy(routingRules.priority) };
  });

  app.post('/routing-rules', async (req) => {
    const principal = guard(req, 'routing.manage');
    const body = z.object({
      nameFa: z.string().min(3), priority: z.number().optional(),
      matchCategoryKey: z.string().optional(), matchKind: z.string().optional(),
      matchSeverity: z.string().optional(), matchRegionId: z.string().uuid().optional(),
      matchKeywords: z.array(z.string()).optional(),
      targetOrganizationId: z.string().uuid().optional(), targetDepartmentId: z.string().uuid().optional(),
      targetGuildId: z.string().uuid().optional(), setPriority: z.string().optional(),
    }).parse(req.body);
    const [row] = await getDb().insert(routingRules).values({ ...body, priority: body.priority ?? 100 }).returning();
    await recordAudit({ action: 'routing.rule_created', actorId: principal.userId, entityType: 'routing_rule', entityId: row.id, after: body });
    return row;
  });

  app.patch('/routing-rules/:id', async (req) => {
    const principal = guard(req, 'routing.manage');
    const { id } = req.params as { id: string };
    const body = z.object({ isActive: z.boolean().optional(), priority: z.number().optional(), nameFa: z.string().optional() }).parse(req.body);
    const [row] = await getDb().update(routingRules).set(body).where(eq(routingRules.id, id)).returning();
    await recordAudit({ action: 'routing.rule_updated', actorId: principal.userId, entityType: 'routing_rule', entityId: id, after: body });
    return row;
  });

  /* ------------------------------ طبقه‌بندی‌ها ------------------------------ */
  app.post('/taxonomies', async (req) => {
    const principal = guard(req, 'taxonomy.manage');
    const body = z.object({
      kind: z.string(), key: z.string(), nameFa: z.string(), parentId: z.string().uuid().optional(),
      icon: z.string().optional(), color: z.string().optional(), orderIndex: z.number().optional(),
    }).parse(req.body);
    const [row] = await getDb().insert(taxonomies).values(body).returning();
    await recordAudit({ action: 'taxonomy.created', actorId: principal.userId, entityType: 'taxonomy', entityId: row.id, after: body });
    return row;
  });

  /* -------------------------------- حسابرسی -------------------------------- */
  app.get('/audit', async (req) => {
    guard(req, 'audit.view');
    const q = req.query as Record<string, string | undefined>;
    const conditions = [];
    if (q.action) conditions.push(eq(auditLogs.action, q.action));
    if (q.entityType) conditions.push(eq(auditLogs.entityType, q.entityType));
    if (q.entityId) conditions.push(eq(auditLogs.entityId, q.entityId));
    const rows = await getDb().select().from(auditLogs)
      .where(conditions.length ? and(...conditions) : undefined)
      .orderBy(desc(auditLogs.createdAt)).limit(Math.min(500, Number(q.limit ?? 100)));
    return { items: rows };
  });

  /* --------------------------------- تحلیل --------------------------------- */
  app.get('/analytics', async (req) => {
    guard(req, 'analytics.view');
    const db = getDb();
    const daily = await db.execute<{ day: string; kind: string; c: number }>(sql`
      SELECT to_char(created_at, 'YYYY-MM-DD') AS day, kind, COUNT(*)::int AS c
      FROM analytics_events WHERE created_at > now() - interval '30 days'
      GROUP BY day, kind ORDER BY day`);
    const responseTime = await db.execute<{ avg_hours: number | null }>(sql`
      SELECT AVG(EXTRACT(EPOCH FROM (closed_at - created_at))/3600) AS avg_hours FROM cases WHERE closed_at IS NOT NULL`);
    const orgActivity = await db.execute<{ name: string; total: number; closed: number }>(sql`
      SELECT o.name_fa AS name, COUNT(c.*)::int AS total,
             COUNT(*) FILTER (WHERE c.status IN ('resolved','closed'))::int AS closed
      FROM organizations o LEFT JOIN cases c ON c.organization_id = o.id
      GROUP BY o.id ORDER BY total DESC LIMIT 12`);
    return {
      daily: daily.rows ?? [],
      avgResolutionHours: responseTime.rows?.[0]?.avg_hours ?? null,
      organizationActivity: orgActivity.rows ?? [],
      note: 'همه اعداد از رویدادهای واقعی همین سامانه محاسبه شده‌اند.',
    };
  });

  /* ----------------------------- سلامت و نگهداری ---------------------------- */
  app.post('/maintenance/reindex', async (req) => {
    guard(req, 'search.manage');
    return { indexed: await reindexAll() };
  });

  app.post('/maintenance/sla-sweep', async (req) => {
    guard(req, 'settings.manage');
    return runSlaSweep();
  });

  app.post('/maintenance/editorial-lock', async (req) => {
    guard(req, 'settings.manage');
    return runEditorialMaintenance();
  });

  app.get('/system/health', async (req) => {
    guard(req, 'analytics.view');
    const db = getDb();
    const size = await db.execute<{ table_name: string; rows: number }>(sql`
      SELECT relname AS table_name, n_live_tup::int AS rows FROM pg_stat_user_tables ORDER BY n_live_tup DESC LIMIT 20`);
    return {
      database: { engine: 'PostgreSQL (PGlite در حالت محلی)', tables: size.rows ?? [] },
      media: { count: (await db.select({ c: sql<number>`count(*)::int` }).from(mediaAssets))[0]?.c ?? 0 },
      searchIndex: (await db.execute<{ c: number }>(sql`SELECT COUNT(*)::int AS c FROM search_documents`)).rows?.[0]?.c ?? 0,
    };
  });

  /* -------------------------------- تنظیمات -------------------------------- */
  app.get('/settings', async (req) => {
    guard(req, 'settings.manage');
    return { items: await getDb().select().from(settings) };
  });

  app.put('/settings/:key', async (req) => {
    const principal = guard(req, 'settings.manage');
    const { key } = req.params as { key: string };
    const { value } = z.object({ value: z.unknown() }).parse(req.body);
    const db = getDb();
    const [existing] = await db.select().from(settings).where(eq(settings.key, key));
    if (existing) await db.update(settings).set({ value, updatedBy: principal.userId, updatedAt: new Date() }).where(eq(settings.key, key));
    else await db.insert(settings).values({ key, value, updatedBy: principal.userId });
    await recordAudit({ action: AuditActions.SETTINGS_CHANGE, actorId: principal.userId, entityType: 'setting', entityId: key, before: existing?.value as any, after: { value } as any });
    return { ok: true };
  });
}
