import { COOKIE_NAME } from "@shared/const";
import { getSessionCookieOptions } from "./_core/cookies";
import { systemRouter } from "./_core/systemRouter";
import { publicProcedure, router, protectedProcedure } from "./_core/trpc";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import * as db from "./db";
import { nanoid } from "nanoid";
import { notifyOwner } from "./_core/notification";

// Admin-only procedure
const adminProcedure = protectedProcedure.use(async ({ ctx, next }) => {
  if (ctx.user.role !== "admin") {
    throw new TRPCError({ code: "FORBIDDEN" });
  }
  return next({ ctx });
});

export const appRouter = router({
  system: systemRouter,
  
  auth: router({
    me: publicProcedure.query(opts => opts.ctx.user),
    logout: publicProcedure.mutation(({ ctx }) => {
      const cookieOptions = getSessionCookieOptions(ctx.req);
      ctx.res.clearCookie(COOKIE_NAME, { ...cookieOptions, maxAge: -1 });
      return { success: true } as const;
    }),
  }),

  // User Profile & Wallet
  user: router({
    getProfile: protectedProcedure.query(async ({ ctx }) => {
      const user = await db.getUserById(ctx.user.id);
      return user;
    }),

    updateProfile: protectedProcedure
      .input(z.object({
        name: z.string().optional(),
        deviceFingerprint: z.string().optional(),
        lastIpAddress: z.string().optional(),
        lastLocation: z.string().optional(),
      }))
      .mutation(async ({ ctx, input }) => {
        // In production, use proper update logic
        return { success: true };
      }),

    getWalletBalance: protectedProcedure.query(async ({ ctx }) => {
      const user = await db.getUserById(ctx.user.id);
      return { balance: user?.walletBalance || "0.00" };
    }),

    getWalletTransactions: protectedProcedure
      .input(z.object({ limit: z.number().default(50), offset: z.number().default(0) }))
      .query(async ({ ctx, input }) => {
        return await db.getWalletTransactions(ctx.user.id, input.limit, input.offset);
      }),
  }),

  // VTU Service
  vtu: router({
    getProviders: protectedProcedure.query(async () => {
      return await db.getVtuProviders();
    }),

    getPricing: protectedProcedure
      .input(z.object({ providerId: z.number() }))
      .query(async ({ input }) => {
        // In production, fetch from external API
        const mockPricing = {
          airtime: [
            { amount: "100", price: "100" },
            { amount: "500", price: "500" },
            { amount: "1000", price: "1000" },
          ],
          data: [
            { amount: "500MB", price: "150" },
            { amount: "1GB", price: "250" },
            { amount: "5GB", price: "1000" },
          ],
        };
        return mockPricing;
      }),

    initiateTransaction: protectedProcedure
      .input(z.object({
        providerId: z.number(),
        type: z.enum(["airtime", "data"]),
        phoneNumber: z.string(),
        amount: z.string(),
      }))
      .mutation(async ({ ctx, input }) => {
        const reference = `VTU-${Date.now()}-${nanoid(8)}`;
        
        // Check wallet balance
        const user = await db.getUserById(ctx.user.id);
        if (!user || parseFloat(user.walletBalance) < parseFloat(input.amount)) {
          throw new TRPCError({ code: "BAD_REQUEST", message: "Insufficient wallet balance" });
        }

        // Create transaction record
        const transaction = await db.createVtuTransaction({
          userId: ctx.user.id,
          providerId: input.providerId,
          type: input.type,
          phoneNumber: input.phoneNumber,
          amount: input.amount,
          status: "pending",
          reference,
        });

        // Deduct from wallet
        const newBalance = (parseFloat(user.walletBalance) - parseFloat(input.amount)).toFixed(2);
        await db.updateWalletBalance(ctx.user.id, newBalance);

        // Create wallet transaction record
        await db.createWalletTransaction({
          userId: ctx.user.id,
          type: "vtu_purchase",
          amount: input.amount,
          balanceBefore: user.walletBalance,
          balanceAfter: newBalance,
          status: "pending",
          reference,
          description: `VTU Purchase - ${input.type} for ${input.phoneNumber}`,
        });

        return { reference, status: "pending" };
      }),

    getTransactionStatus: protectedProcedure
      .input(z.object({ reference: z.string() }))
      .query(async ({ input }) => {
        const transaction = await db.getVtuTransactionByReference(input.reference);
        if (!transaction) {
          throw new TRPCError({ code: "NOT_FOUND", message: "Transaction not found" });
        }
        return transaction;
      }),

    getTransactionHistory: protectedProcedure
      .input(z.object({ limit: z.number().default(50), offset: z.number().default(0) }))
      .query(async ({ ctx, input }) => {
        return await db.getVtuTransactions(ctx.user.id, input.limit, input.offset);
      }),
  }),

  // Gift Cards Service
  giftcard: router({
    getProviders: protectedProcedure.query(async () => {
      return await db.getGiftCardProviders();
    }),

    getDenominations: protectedProcedure
      .input(z.object({ providerId: z.number() }))
      .query(async ({ input }) => {
        return await db.getGiftCardDenominations(input.providerId);
      }),

    purchaseGiftCard: protectedProcedure
      .input(z.object({
        denominationId: z.number(),
        quantity: z.number().min(1),
      }))
      .mutation(async ({ ctx, input }) => {
        const reference = `GC-${Date.now()}-${nanoid(8)}`;
        
        // Get user and check balance
        const user = await db.getUserById(ctx.user.id);
        if (!user) throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });

        // Get denomination details
        const denominations = await db.getGiftCardDenominations(0); // Would need providerId
        if (denominations.length === 0) {
          throw new TRPCError({ code: "NOT_FOUND", message: "Denomination not found" });
        }

        const denomination = denominations[0];
        const totalAmount = (parseFloat(denomination.price) * input.quantity).toFixed(2);

        if (parseFloat(user.walletBalance) < parseFloat(totalAmount)) {
          throw new TRPCError({ code: "BAD_REQUEST", message: "Insufficient wallet balance" });
        }

        // Create transaction
        const transaction = await db.createGiftCardTransaction({
          userId: ctx.user.id,
          denominationId: input.denominationId,
          quantity: input.quantity,
          totalAmount,
          status: "pending",
          reference,
        });

        // Deduct from wallet
        const newBalance = (parseFloat(user.walletBalance) - parseFloat(totalAmount)).toFixed(2);
        await db.updateWalletBalance(ctx.user.id, newBalance);

        // Create wallet transaction
        await db.createWalletTransaction({
          userId: ctx.user.id,
          type: "giftcard_purchase",
          amount: totalAmount,
          balanceBefore: user.walletBalance,
          balanceAfter: newBalance,
          status: "pending",
          reference,
          description: `Gift Card Purchase - ${input.quantity} card(s)`,
        });

        return { reference, status: "pending", totalAmount };
      }),

    getTransactionHistory: protectedProcedure
      .input(z.object({ limit: z.number().default(50), offset: z.number().default(0) }))
      .query(async ({ ctx, input }) => {
        return await db.getGiftCardTransactions(ctx.user.id, input.limit, input.offset);
      }),
  }),

  // Customer Support
  support: router({
    createTicket: protectedProcedure
      .input(z.object({
        subject: z.string(),
        category: z.string(),
        description: z.string(),
        priority: z.enum(["low", "medium", "high", "urgent"]).optional(),
      }))
      .mutation(async ({ ctx, input }) => {
        const ticketNumber = `TKT-${Date.now()}-${nanoid(6)}`;
        
        const ticket = await db.createSupportTicket({
          userId: ctx.user.id,
          ticketNumber,
          subject: input.subject,
          category: input.category,
          description: input.description,
          priority: input.priority || "medium",
          status: "open",
        });

        return { ticketNumber, status: "open" };
      }),

    getTickets: protectedProcedure
      .input(z.object({ limit: z.number().default(50), offset: z.number().default(0) }))
      .query(async ({ ctx, input }) => {
        return await db.getSupportTickets(ctx.user.id, input.limit, input.offset);
      }),

    getTicketDetail: protectedProcedure
      .input(z.object({ ticketId: z.number() }))
      .query(async ({ ctx, input }) => {
        const ticket = await db.getSupportTicketById(input.ticketId);
        if (!ticket || ticket.userId !== ctx.user.id) {
          throw new TRPCError({ code: "FORBIDDEN", message: "Access denied" });
        }
        const messages = await db.getTicketMessages(input.ticketId);
        return { ticket, messages };
      }),

    addMessage: protectedProcedure
      .input(z.object({
        ticketId: z.number(),
        message: z.string(),
      }))
      .mutation(async ({ ctx, input }) => {
        const ticket = await db.getSupportTicketById(input.ticketId);
        if (!ticket || ticket.userId !== ctx.user.id) {
          throw new TRPCError({ code: "FORBIDDEN", message: "Access denied" });
        }

        await db.addTicketMessage({
          ticketId: input.ticketId,
          userId: ctx.user.id,
          message: input.message,
        });

        return { success: true };
      }),

    getFaqs: publicProcedure
      .input(z.object({ category: z.string().optional() }))
      .query(async ({ input }) => {
        return await db.getFaqs(input.category);
      }),

    getFaqCategories: publicProcedure.query(async () => {
      return await db.getFaqCategories();
    }),
  }),

  // Fraud Detection & Admin
  fraud: router({
    createRule: adminProcedure
      .input(z.object({
        name: z.string(),
        description: z.string().optional(),
        ruleType: z.enum(["velocity", "amount_threshold", "device_fingerprint", "geo_anomaly", "custom"]),
        condition: z.string(),
        action: z.enum(["alert", "block", "review"]),
        severity: z.enum(["low", "medium", "high", "critical"]),
        parameters: z.string().optional(),
      }))
      .mutation(async ({ ctx, input }) => {
        const rule = await db.createFraudRule({
          name: input.name,
          description: input.description,
          ruleType: input.ruleType,
          condition: input.condition,
          action: input.action,
          severity: input.severity,
          parameters: input.parameters,
          createdBy: ctx.user.id,
        });

        await db.createAuditLog({
          adminId: ctx.user.id,
          action: "CREATE_FRAUD_RULE",
          resourceType: "fraud_rule",
          details: `Created rule: ${input.name}`,
        });

        return { success: true };
      }),

    getRules: adminProcedure.query(async () => {
      return await db.getFraudRules();
    }),

    updateRule: adminProcedure
      .input(z.object({
        ruleId: z.number(),
        updates: z.object({
          isEnabled: z.boolean().optional(),
          condition: z.string().optional(),
          action: z.enum(["alert", "block", "review"]).optional(),
        }),
      }))
      .mutation(async ({ ctx, input }) => {
        await db.updateFraudRule(input.ruleId, input.updates);

        await db.createAuditLog({
          adminId: ctx.user.id,
          action: "UPDATE_FRAUD_RULE",
          resourceType: "fraud_rule",
          resourceId: input.ruleId.toString(),
          details: JSON.stringify(input.updates),
        });

        return { success: true };
      }),

    getAlerts: adminProcedure
      .input(z.object({ limit: z.number().default(100), offset: z.number().default(0) }))
      .query(async ({ input }) => {
        return await db.getFraudAlerts(input.limit, input.offset);
      }),

    reviewAlert: adminProcedure
      .input(z.object({
        alertId: z.number(),
        status: z.enum(["reviewed", "resolved", "false_positive"]),
        notes: z.string().optional(),
      }))
      .mutation(async ({ ctx, input }) => {
        await db.updateFraudAlert(input.alertId, {
          status: input.status,
          reviewedBy: ctx.user.id,
          reviewedAt: new Date(),
          notes: input.notes,
        });

        await db.createAuditLog({
          adminId: ctx.user.id,
          action: "REVIEW_FRAUD_ALERT",
          resourceType: "fraud_alert",
          resourceId: input.alertId.toString(),
          details: `Status: ${input.status}`,
        });

        return { success: true };
      }),
  }),

  // Admin Dashboard
  admin: router({
    getUsers: adminProcedure
      .input(z.object({ limit: z.number().default(50), offset: z.number().default(0) }))
      .query(async ({ input }) => {
        return await db.getAllUsers(input.limit, input.offset);
      }),

    getUserDetail: adminProcedure
      .input(z.object({ userId: z.number() }))
      .query(async ({ input }) => {
        const user = await db.getUserById(input.userId);
        if (!user) throw new TRPCError({ code: "NOT_FOUND", message: "User not found" });
        
        const transactions = await db.getWalletTransactions(input.userId, 100, 0);
        const vtuTransactions = await db.getVtuTransactions(input.userId, 50, 0);
        const giftcardTransactions = await db.getGiftCardTransactions(input.userId, 50, 0);
        
        return { user, transactions, vtuTransactions, giftcardTransactions };
      }),

    updateUserStatus: adminProcedure
      .input(z.object({ userId: z.number(), isActive: z.boolean() }))
      .mutation(async ({ ctx, input }) => {
        await db.updateUserStatus(input.userId, input.isActive);

        await db.createAuditLog({
          adminId: ctx.user.id,
          action: input.isActive ? "ACTIVATE_USER" : "SUSPEND_USER",
          resourceType: "user",
          resourceId: input.userId.toString(),
        });

        return { success: true };
      }),

    updateUserRole: adminProcedure
      .input(z.object({ userId: z.number(), role: z.enum(["user", "admin"]) }))
      .mutation(async ({ ctx, input }) => {
        await db.updateUserRole(input.userId, input.role);

        await db.createAuditLog({
          adminId: ctx.user.id,
          action: "UPDATE_USER_ROLE",
          resourceType: "user",
          resourceId: input.userId.toString(),
          details: `Role: ${input.role}`,
        });

        return { success: true };
      }),

    getTransactions: adminProcedure
      .input(z.object({ limit: z.number().default(100), offset: z.number().default(0) }))
      .query(async ({ input }) => {
        return await db.getAllTransactions(input.limit, input.offset);
      }),

    searchTransactions: adminProcedure
      .input(z.object({ query: z.string() }))
      .query(async ({ input }) => {
        return await db.searchTransactions(input.query, 50);
      }),

    getAuditLogs: adminProcedure
      .input(z.object({ limit: z.number().default(100), offset: z.number().default(0) }))
      .query(async ({ input }) => {
        return await db.getAuditLogs(input.limit, input.offset);
      }),

    getDashboardStats: adminProcedure.query(async () => {
      const allUsers = await db.getAllUsers(1000, 0);
      const allTransactions = await db.getAllTransactions(1000, 0);
      
      const totalUsers = allUsers.length;
      const activeUsers = allUsers.filter(u => u.isActive).length;
      const totalTransactionAmount = allTransactions.reduce((sum, t) => sum + parseFloat(t.amount), 0);
      const successfulTransactions = allTransactions.filter(t => t.status === "success").length;

      return {
        totalUsers,
        activeUsers,
        totalTransactionAmount: totalTransactionAmount.toFixed(2),
        successfulTransactions,
        totalTransactions: allTransactions.length,
      };
    }),
  }),
});

export type AppRouter = typeof appRouter;
