var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};

// server/email-service.ts
import nodemailer from "nodemailer";
function createTransporter() {
  const emailUser = process.env.EMAIL_USER;
  const emailPass = process.env.EMAIL_PASS;
  const emailHost = process.env.EMAIL_HOST || "smtp.gmail.com";
  const emailPort = parseInt(process.env.EMAIL_PORT || "587");
  if (!emailUser || !emailPass) {
    console.log("\u26A0\uFE0F  Email credentials not configured, using console logging");
    return null;
  }
  try {
    const transporter = nodemailer.createTransport({
      host: emailHost,
      port: emailPort,
      secure: emailPort === 465,
      auth: {
        user: emailUser,
        pass: emailPass
      }
    });
    console.log("\u2705 Email transporter configured");
    return transporter;
  } catch (error) {
    console.error("\u274C Failed to create email transporter:", error);
    return null;
  }
}
var init_email_service = __esm({
  "server/email-service.ts"() {
    "use strict";
  }
});

// server/sms-service.ts
var sms_service_exports = {};
__export(sms_service_exports, {
  smsService: () => smsService
});
var MSG91SMSProvider, EmailOTPProvider, SMSService, smsService;
var init_sms_service = __esm({
  "server/sms-service.ts"() {
    "use strict";
    init_email_service();
    MSG91SMSProvider = class {
      authKey;
      templateId;
      constructor() {
        this.authKey = process.env.MSG91_AUTH_KEY || "462769AcJdGeJIRl68933254P1";
        this.templateId = process.env.MSG91_TEMPLATE_ID || "6894776284c42857d572d99b";
        console.log("\u{1F50D} MSG91 Environment check:");
        console.log("   AUTH_KEY from env:", process.env.MSG91_AUTH_KEY ? "FOUND" : "NOT FOUND");
        console.log("   TEMPLATE_ID from env:", process.env.MSG91_TEMPLATE_ID ? "FOUND" : "NOT FOUND");
        console.log("   Using AUTH_KEY:", this.authKey.substring(0, 10) + "...");
        console.log("   Using TEMPLATE_ID:", this.templateId);
        if (this.authKey && this.templateId) {
          console.log("\u2705 MSG91 SMS service initialized with credentials");
        } else {
          console.log("\u26A0\uFE0F  MSG91 credentials not configured properly");
        }
      }
      // Format phone number to ensure proper country code
      formatPhoneNumber(phoneNumber) {
        const cleanNumber = phoneNumber.replace(/\D/g, "");
        if (cleanNumber.startsWith("91") && cleanNumber.length === 12) {
          return cleanNumber;
        }
        if (cleanNumber.length === 10) {
          return "91" + cleanNumber;
        }
        return cleanNumber;
      }
      async sendSMS(to, message) {
        if (!this.authKey || !this.templateId) {
          console.log(`\u{1F4F1} SMS (Console Mode - MSG91) to ${to}: ${message}`);
          return true;
        }
        try {
          const otpMatch = message.match(/\d{6}/);
          const otp = otpMatch ? otpMatch[0] : "123456";
          const fetch2 = (await import("node-fetch")).default;
          const response = await fetch2("https://api.msg91.com/api/v5/flow/", {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              "authkey": this.authKey
            },
            body: JSON.stringify({
              template_id: this.templateId,
              // DLT approved template ID
              recipients: [
                {
                  mobiles: this.formatPhoneNumber(to),
                  // Ensure proper format with country code
                  var: otp
                  // Standard MSG91 variable format
                }
              ]
            })
          });
          const result = await response.json();
          if (response.ok && result.type === "success") {
            console.log(`\u2705 MSG91 SMS sent successfully to ${to}`);
            return true;
          } else {
            console.error("\u274C MSG91 SMS failed:", result);
            return false;
          }
        } catch (error) {
          console.error("\u274C Failed to send MSG91 SMS:", error);
          return false;
        }
      }
    };
    EmailOTPProvider = class {
      async sendSMS(to, message) {
        try {
          const transporter = createTransporter();
          if (!transporter) {
            console.log(`\u{1F4E7} Email OTP (Console Mode) to ${to}: ${message}`);
            return true;
          }
          await transporter.sendMail({
            from: process.env.EMAIL_FROM || "noreply@myymotto.com",
            to,
            subject: "MyyMotto - Your Verification Code",
            html: `
          <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
            <h2 style="color: #ff6b35;">MyyMotto Verification</h2>
            <p>Your verification code is:</p>
            <div style="background: #f5f5f5; padding: 20px; text-align: center; font-size: 24px; font-weight: bold; letter-spacing: 3px; border-radius: 8px; margin: 20px 0;">
              ${message.match(/\d{6}/)?.[0] || "ERROR"}
            </div>
            <p style="color: #666;">This code will expire in 5 minutes.</p>
            <p style="color: #666;">If you didn't request this code, please ignore this email.</p>
          </div>
        `
          });
          console.log(`\u2705 Email OTP sent successfully to ${to}`);
          return true;
        } catch (error) {
          console.error("\u274C Failed to send email OTP:", error);
          console.log(`\u{1F4E7} Email OTP (Console Mode) to ${to}: ${message}`);
          return true;
        }
      }
    };
    SMSService = class {
      provider;
      constructor() {
        this.provider = new MSG91SMSProvider();
        console.log("\u{1F4F1} Initializing MSG91 SMS service (Indian provider with DLT approval)");
        if (!process.env.MSG91_AUTH_KEY || !process.env.MSG91_TEMPLATE_ID) {
          console.log("\u{1F4F1} Using fallback MSG91 credentials (production ready)");
        }
      }
      async sendOTP(identifier, otp) {
        const isEmail = identifier.includes("@");
        const isMobile = /^\+?[\d\s-()]+$/.test(identifier.trim());
        if (isEmail) {
          const emailProvider = new EmailOTPProvider();
          return await emailProvider.sendSMS(identifier, `Your MyyMotto verification code is: ${otp}`);
        } else if (isMobile) {
          return await this.provider.sendSMS(identifier, `Your MyyMotto verification code is: ${otp}. Valid for 5 minutes.`);
        } else {
          console.error("\u274C Invalid identifier format:", identifier);
          return false;
        }
      }
    };
    smsService = new SMSService();
  }
});

// server/index.ts
import express2 from "express";

// server/routes.ts
import { createServer } from "http";

// shared/schema.ts
var schema_exports = {};
__export(schema_exports, {
  accountInformation: () => accountInformation,
  adminMessages: () => adminMessages,
  biometricSetupSchema: () => biometricSetupSchema,
  broadcastResponses: () => broadcastResponses,
  broadcasts: () => broadcasts,
  calendarReminders: () => calendarReminders,
  dashboardWidgets: () => dashboardWidgets,
  documentExpiries: () => documentExpiries,
  documents: () => documents,
  emergencyContacts: () => emergencyContacts,
  insertAccountInformationSchema: () => insertAccountInformationSchema,
  insertAdminMessageSchema: () => insertAdminMessageSchema,
  insertBroadcastResponseSchema: () => insertBroadcastResponseSchema,
  insertBroadcastSchema: () => insertBroadcastSchema,
  insertCalendarReminderSchema: () => insertCalendarReminderSchema,
  insertDashboardWidgetSchema: () => insertDashboardWidgetSchema,
  insertDocumentExpirySchema: () => insertDocumentExpirySchema,
  insertDocumentSchema: () => insertDocumentSchema,
  insertEmergencyContactSchema: () => insertEmergencyContactSchema,
  insertInvoiceSchema: () => insertInvoiceSchema,
  insertMaintenanceRecordSchema: () => insertMaintenanceRecordSchema,
  insertMaintenanceScheduleSchema: () => insertMaintenanceScheduleSchema,
  insertNewsItemSchema: () => insertNewsItemSchema,
  insertNewsUpdateLogSchema: () => insertNewsUpdateLogSchema,
  insertNotificationSchema: () => insertNotificationSchema,
  insertOtpSchema: () => insertOtpSchema,
  insertPaymentHistorySchema: () => insertPaymentHistorySchema,
  insertRatingSchema: () => insertRatingSchema,
  insertServiceAlertSchema: () => insertServiceAlertSchema,
  insertServiceLogSchema: () => insertServiceLogSchema,
  insertSubscriptionNotificationSchema: () => insertSubscriptionNotificationSchema,
  insertSubscriptionSchema: () => insertSubscriptionSchema,
  insertTrafficViolationSchema: () => insertTrafficViolationSchema,
  insertUserMessageSchema: () => insertUserMessageSchema,
  insertUserProfileSchema: () => insertUserProfileSchema,
  insertUserSchema: () => insertUserSchema,
  insertVehicleSchema: () => insertVehicleSchema,
  invoices: () => invoices,
  maintenanceRecords: () => maintenanceRecords,
  maintenanceSchedules: () => maintenanceSchedules,
  newsItems: () => newsItems,
  newsUpdateLog: () => newsUpdateLog,
  notifications: () => notifications,
  otpVerifications: () => otpVerifications,
  paymentHistory: () => paymentHistory,
  pinLoginSchema: () => pinLoginSchema,
  ratings: () => ratings,
  serviceAlerts: () => serviceAlerts,
  serviceLogs: () => serviceLogs,
  setPinSchema: () => setPinSchema,
  signInSchema: () => signInSchema,
  subscriptionNotifications: () => subscriptionNotifications,
  subscriptions: () => subscriptions,
  trafficViolations: () => trafficViolations,
  userMessages: () => userMessages,
  userProfiles: () => userProfiles,
  users: () => users,
  vehicles: () => vehicles,
  verifyOtpSchema: () => verifyOtpSchema
});
import { pgTable, text, serial, integer, boolean, timestamp, date, jsonb } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
var vehicles = pgTable("vehicles", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id),
  // Link to user - temporarily nullable for migration
  make: text("make").notNull(),
  model: text("model").notNull(),
  year: integer("year").notNull(),
  color: text("color").notNull(),
  licensePlate: text("license_plate").notNull().unique(),
  speedometerReading: integer("speedometer_reading"),
  chassisNumber: text("chassis_number"),
  engineNumber: text("engine_number"),
  ownerName: text("owner_name").notNull(),
  ownerPhone: text("owner_phone"),
  thumbnailPath: text("thumbnail_path"),
  insuranceCompany: text("insurance_company"),
  insuranceExpiry: date("insurance_expiry"),
  // Issue date
  insuranceExpiryDate: date("insurance_expiry_date"),
  // Actual expiry date
  insuranceSumInsured: text("insurance_sum_insured"),
  // User-entered sum insured
  insurancePremiumAmount: text("insurance_premium_amount"),
  // User-entered premium
  emissionExpiry: date("emission_expiry"),
  rcExpiry: date("rc_expiry"),
  lastServiceDate: date("last_service_date"),
  currentOdometerReading: integer("current_odometer_reading"),
  averageUsagePerMonth: integer("average_usage_per_month"),
  serviceIntervalKms: integer("service_interval_kms"),
  serviceIntervalMonths: integer("service_interval_months"),
  vehicleType: text("vehicle_type").notNull(),
  // "2-wheeler", "3-wheeler", "4-wheeler" - REQUIRED
  fuelType: text("fuel_type"),
  // "petrol", "diesel", "electric", "hybrid"
  userType: text("user_type").notNull().default("Private"),
  // "Private", "Commercial", "Taxi services" - REQUIRED
  // OCR extracted insurance data
  ocrPolicyNumber: text("ocr_policy_number"),
  ocrSumInsured: text("ocr_sum_insured"),
  ocrPremiumAmount: text("ocr_premium_amount"),
  ocrInsuredName: text("ocr_insured_name"),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var documents = pgTable("documents", {
  id: serial("id").primaryKey(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id).notNull(),
  type: text("type").notNull(),
  // 'rc', 'insurance', 'emission', 'license', 'other'
  fileName: text("file_name").notNull(),
  filePath: text("file_path").notNull(),
  fileSize: integer("file_size").notNull(),
  mimeType: text("mime_type").notNull(),
  uploadedAt: timestamp("uploaded_at").defaultNow().notNull()
});
var maintenanceSchedules = pgTable("maintenance_schedules", {
  id: serial("id").primaryKey(),
  make: text("make").notNull(),
  model: text("model").notNull(),
  year: integer("year").notNull(),
  scheduleData: jsonb("schedule_data").notNull(),
  // Store maintenance schedule JSON from APIs
  source: text("source").notNull(),
  // 'vehicledatabases.com', 'oem', 'manual'
  drivingCondition: text("driving_condition").notNull(),
  // 'normal', 'severe'
  lastUpdated: timestamp("last_updated").defaultNow().notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var newsItems = pgTable("news_items", {
  id: serial("id").primaryKey(),
  title: text("title").notNull(),
  summary: text("summary").notNull(),
  category: text("category").notNull(),
  // 'policy', 'launch', 'news'
  date: text("date").notNull(),
  source: text("source").notNull(),
  link: text("link").notNull(),
  priority: text("priority").notNull(),
  // 'high', 'medium', 'low'
  createdAt: timestamp("created_at").defaultNow().notNull(),
  lastUpdated: timestamp("last_updated").defaultNow().notNull()
});
var calendarReminders = pgTable("calendar_reminders", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  title: text("title").notNull(),
  details: text("details").notNull(),
  reminderDate: timestamp("reminder_date").notNull(),
  notificationSent: boolean("notification_sent").default(false).notNull(),
  isCompleted: boolean("is_completed").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var newsUpdateLog = pgTable("news_update_log", {
  id: serial("id").primaryKey(),
  updateType: text("update_type").notNull(),
  // 'scheduled', 'manual', 'api'
  source: text("source").notNull(),
  // 'perplexity', 'fallback'
  itemsUpdated: integer("items_updated").notNull(),
  lastUpdated: timestamp("last_updated").defaultNow().notNull()
});
var maintenanceRecords = pgTable("maintenance_records", {
  id: serial("id").primaryKey(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id).notNull(),
  maintenanceType: text("maintenance_type").notNull(),
  // 'oil_change', 'tire_change', 'battery_replacement', etc.
  completedDate: date("completed_date"),
  warrantyCardPath: text("warranty_card_path"),
  // Path to uploaded warranty card image
  invoicePath: text("invoice_path"),
  // Path to uploaded invoice image
  notes: text("notes"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var serviceLogs = pgTable("service_logs", {
  id: serial("id").primaryKey(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id).notNull(),
  serviceType: text("service_type").notNull(),
  // Type of service performed
  serviceDate: date("service_date").notNull(),
  serviceCentre: text("service_centre").notNull(),
  currentSpeedometerReading: integer("current_speedometer_reading"),
  // Current speedometer reading at time of service
  billAmount: integer("bill_amount"),
  // Service bill amount in paise (₹100 = 10000 paise)
  serviceIntervalMonths: integer("service_interval_months"),
  // Months to next service (only for General Service (Paid))
  notes: text("notes"),
  invoicePath: text("invoice_path"),
  // Path to uploaded invoice image
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var serviceAlerts = pgTable("service_alerts", {
  id: serial("id").primaryKey(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id).notNull(),
  eventName: text("event_name").notNull(),
  // Name of the scheduled service/event
  scheduledDate: date("scheduled_date").notNull(),
  // When the service is scheduled
  notes: text("notes"),
  // Short notes about the alert
  isActive: boolean("is_active").default(true).notNull(),
  // Whether alert is active
  isCompleted: boolean("is_completed").default(false).notNull(),
  // Whether the service was completed
  notificationSent: boolean("notification_sent").default(false).notNull(),
  // Whether 1-day prior notification was sent
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var subscriptions = pgTable("subscriptions", {
  id: serial("id").primaryKey(),
  userId: text("user_id").notNull(),
  // Reference to users table
  subscriptionType: text("subscription_type").notNull().default("premium"),
  // "premium", "basic"
  subscriptionDate: timestamp("subscription_date").defaultNow().notNull(),
  expiryDate: timestamp("expiry_date").notNull(),
  isActive: boolean("is_active").default(true).notNull(),
  razorpaySubscriptionId: text("razorpay_subscription_id"),
  amount: integer("amount").notNull().default(1e4),
  // Amount in paise (₹100 = 10000 paise)
  currency: text("currency").notNull().default("INR"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var paymentHistory = pgTable("payment_history", {
  id: serial("id").primaryKey(),
  userId: text("user_id").notNull(),
  // Reference to users table
  subscriptionId: integer("subscription_id").references(() => subscriptions.id),
  amount: integer("amount").notNull(),
  // Amount in paise
  currency: text("currency").notNull().default("INR"),
  paymentDate: timestamp("payment_date").defaultNow().notNull(),
  razorpayPaymentId: text("razorpay_payment_id"),
  razorpayOrderId: text("razorpay_order_id"),
  transactionId: text("transaction_id"),
  paymentStatus: text("payment_status").notNull(),
  // "success", "failed", "pending"
  paymentMethod: text("payment_method"),
  // "card", "netbanking", "upi", "wallet"
  invoiceGenerated: boolean("invoice_generated").default(false).notNull(),
  invoicePath: text("invoice_path"),
  // Path to generated invoice PDF
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var accountInformation = pgTable("account_information", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  accountType: text("account_type").notNull().default("individual"),
  // "individual", "business"
  subscriptionPlan: text("subscription_plan").notNull().default("premium"),
  // "free", "premium"
  accountStatus: text("account_status").notNull().default("active"),
  // "active", "suspended", "deactivated"
  totalSubscriptionPayments: integer("total_subscription_payments").notNull().default(0),
  totalAmountPaid: integer("total_amount_paid").notNull().default(0),
  // Total in paise
  lastPaymentDate: timestamp("last_payment_date"),
  nextBillingDate: timestamp("next_billing_date"),
  autoRenewal: boolean("auto_renewal").default(false).notNull(),
  billingAddress: text("billing_address"),
  gstNumber: text("gst_number"),
  // For business accounts
  panNumber: text("pan_number"),
  // For tax compliance
  preferredPaymentMethod: text("preferred_payment_method"),
  // "card", "upi", "netbanking"
  accountNotes: text("account_notes"),
  // Admin notes
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var invoices = pgTable("invoices", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  paymentId: integer("payment_id").references(() => paymentHistory.id),
  subscriptionId: integer("subscription_id").references(() => subscriptions.id),
  invoiceNumber: text("invoice_number").notNull().unique(),
  // INV-2025-001
  invoiceType: text("invoice_type").notNull().default("subscription"),
  // "subscription", "renewal", "upgrade"
  invoiceStatus: text("invoice_status").notNull().default("generated"),
  // "generated", "sent", "paid", "overdue", "cancelled"
  invoiceDate: timestamp("invoice_date").defaultNow().notNull(),
  dueDate: timestamp("due_date").notNull(),
  amount: integer("amount").notNull(),
  // Amount in paise
  taxAmount: integer("tax_amount").notNull().default(0),
  // GST/Tax in paise
  totalAmount: integer("total_amount").notNull(),
  // Total including tax in paise
  currency: text("currency").notNull().default("INR"),
  description: text("description").notNull(),
  // "Myymotto Premium Subscription - 1 Year"
  billingPeriodStart: timestamp("billing_period_start").notNull(),
  billingPeriodEnd: timestamp("billing_period_end").notNull(),
  customerName: text("customer_name").notNull(),
  customerEmail: text("customer_email"),
  customerPhone: text("customer_phone").notNull(),
  customerAddress: text("customer_address"),
  invoiceFilePath: text("invoice_file_path"),
  // PDF file path
  downloadCount: integer("download_count").notNull().default(0),
  lastDownloaded: timestamp("last_downloaded"),
  emailSent: boolean("email_sent").default(false).notNull(),
  emailSentAt: timestamp("email_sent_at"),
  remindersSent: integer("reminders_sent").notNull().default(0),
  lastReminderSent: timestamp("last_reminder_sent"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var subscriptionNotifications = pgTable("subscription_notifications", {
  id: serial("id").primaryKey(),
  userId: text("user_id").notNull(),
  subscriptionId: integer("subscription_id").references(() => subscriptions.id),
  notificationType: text("notification_type").notNull(),
  // "expiry_warning", "expired", "renewal_reminder"
  sentDate: timestamp("sent_date").defaultNow().notNull(),
  isRead: boolean("is_read").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var documentExpiries = pgTable("document_expiries", {
  id: serial("id").primaryKey(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id).notNull(),
  userId: integer("user_id").references(() => users.id).notNull(),
  documentType: text("document_type").notNull(),
  // "road_tax", "fitness_certificate", "travel_permits", "emission", "rc_book", "insurance"
  expiryDate: date("expiry_date").notNull(),
  amount: integer("amount"),
  // Amount paid for document in paise
  issueDate: date("issue_date"),
  reminderSent: boolean("reminder_sent").default(false).notNull(),
  lastReminderDate: timestamp("last_reminder_date"),
  isActive: boolean("is_active").default(true).notNull(),
  // False when renewed
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var insertVehicleSchema = createInsertSchema(vehicles).omit({
  id: true,
  createdAt: true
}).extend({
  year: z.number().min(1900).max((/* @__PURE__ */ new Date()).getFullYear() + 1),
  licensePlate: z.string().min(1, "License plate is required"),
  chassisNumber: z.string().optional().nullable(),
  engineNumber: z.string().optional().nullable(),
  ownerName: z.string().min(1, "Owner name is required"),
  ownerPhone: z.string().optional().nullable(),
  thumbnailPath: z.string().optional().nullable(),
  insuranceCompany: z.string().optional().nullable(),
  insuranceExpiry: z.string().optional().nullable(),
  // Insurance expiry can be in future
  insuranceExpiryDate: z.string().optional().nullable(),
  insuranceSumInsured: z.string().optional().nullable(),
  insurancePremiumAmount: z.string().optional().nullable(),
  emissionExpiry: z.string().optional().nullable().refine((date2) => {
    if (!date2) return true;
    const selectedDate = new Date(date2);
    const today = /* @__PURE__ */ new Date();
    today.setHours(0, 0, 0, 0);
    return selectedDate <= today;
  }, "Emission date cannot be in the future"),
  rcExpiry: z.string().optional().nullable(),
  // RC expiry can be in future
  lastServiceDate: z.string().optional().nullable().refine((date2) => {
    if (!date2) return true;
    const selectedDate = new Date(date2);
    const today = /* @__PURE__ */ new Date();
    today.setHours(0, 0, 0, 0);
    return selectedDate <= today;
  }, "Last service date cannot be in the future"),
  currentOdometerReading: z.number().optional().nullable(),
  averageUsagePerMonth: z.number().optional().nullable(),
  serviceIntervalKms: z.number().optional().nullable(),
  serviceIntervalMonths: z.number().optional().nullable(),
  vehicleType: z.string().min(1, "Vehicle type is required"),
  fuelType: z.string().optional().nullable()
});
var insertDocumentSchema = createInsertSchema(documents).omit({
  id: true,
  uploadedAt: true
});
var insertMaintenanceScheduleSchema = createInsertSchema(maintenanceSchedules).omit({
  id: true,
  createdAt: true,
  lastUpdated: true
});
var insertMaintenanceRecordSchema = createInsertSchema(maintenanceRecords).omit({
  id: true,
  createdAt: true,
  updatedAt: true
}).extend({
  completedDate: z.string().optional().nullable()
});
var insertServiceLogSchema = createInsertSchema(serviceLogs).omit({
  id: true,
  createdAt: true,
  updatedAt: true
}).extend({
  serviceDate: z.string().min(1, "Service date is required"),
  serviceType: z.string().min(1, "Service type is required"),
  serviceCentre: z.string().min(1, "Service centre is required")
});
var insertDocumentExpirySchema = createInsertSchema(documentExpiries).omit({
  id: true,
  createdAt: true,
  updatedAt: true
}).extend({
  expiryDate: z.string().min(1, "Expiry date is required"),
  issueDate: z.string().optional().nullable()
});
var users = pgTable("users", {
  id: serial("id").primaryKey(),
  username: text("username").notNull().unique(),
  password: text("password").notNull(),
  email: text("email"),
  mobile: text("mobile"),
  isVerified: boolean("is_verified").default(false).notNull(),
  pin: text("pin"),
  // 4-6 digit PIN for quick login
  biometricEnabled: boolean("biometric_enabled").default(false).notNull(),
  subscriptionStatus: text("subscription_status").default("free"),
  // free, starter, premium, professional
  subscriptionExpiry: timestamp("subscription_expiry"),
  vehicleLimit: integer("vehicle_limit").default(2),
  // Free plan: 2 vehicles
  razorpayOrderId: text("razorpay_order_id"),
  razorpayPaymentId: text("razorpay_payment_id"),
  isAdmin: boolean("is_admin").default(false),
  // Admin access flag
  adminRole: text("admin_role").default("user"),
  // 'user', 'admin', 'super_admin'
  isBlocked: boolean("is_blocked").default(false),
  // User blocking status
  blockedAt: timestamp("blocked_at"),
  // When user was blocked
  blockedReason: text("blocked_reason"),
  // Reason for blocking
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var otpVerifications = pgTable("otp_verifications", {
  id: serial("id").primaryKey(),
  identifier: text("identifier").notNull(),
  // mobile or email
  otp: text("otp").notNull(),
  type: text("type").notNull(),
  // 'mobile' or 'email'
  expiresAt: timestamp("expires_at").notNull(),
  isUsed: boolean("is_used").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var userProfiles = pgTable("user_profiles", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  name: text("name").notNull(),
  age: integer("age").notNull(),
  gender: text("gender"),
  // "male", "female", "other", "prefer_not_to_say"
  address: text("address").notNull(),
  bloodGroup: text("blood_group").notNull(),
  state: text("state").notNull(),
  city: text("city").notNull(),
  pinCode: text("pin_code").notNull(),
  alternatePhone: text("alternate_phone"),
  email: text("email").notNull(),
  profilePicture: text("profile_picture"),
  driversLicenseNumber: text("drivers_license_number"),
  driversLicenseCopy: text("drivers_license_copy"),
  driversLicenseValidTill: date("drivers_license_valid_till"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var dashboardWidgets = pgTable("dashboard_widgets", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  widgetType: text("widget_type").notNull(),
  // "stats", "quick_actions", "recent_vehicles", "news", "reminders"
  position: integer("position").notNull().default(0),
  isVisible: boolean("is_visible").notNull().default(true),
  size: text("size").default("medium"),
  // "small", "medium", "large"
  createdAt: timestamp("created_at").defaultNow(),
  updatedAt: timestamp("updated_at").defaultNow()
});
var ratings = pgTable("ratings", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  userName: text("user_name").notNull(),
  phoneNumber: text("phone_number").notNull(),
  emailId: text("email_id").notNull(),
  rating: integer("rating").notNull(),
  // 1-5 stars
  feedback: text("feedback"),
  // optional feedback text
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var insertUserSchema = createInsertSchema(users).pick({
  username: true,
  password: true,
  email: true,
  mobile: true
});
var signInSchema = z.object({
  identifier: z.string().min(1, "Mobile number or email is required").refine((val) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (emailRegex.test(val)) return true;
    const cleanMobile = val.replace(/\D/g, "");
    return cleanMobile.length >= 10;
  }, "Enter a valid email or mobile number (at least 10 digits)")
});
var verifyOtpSchema = z.object({
  identifier: z.string().min(1, "Mobile number or email is required"),
  otp: z.string().length(6, "OTP must be 6 digits")
});
var setPinSchema = z.object({
  pin: z.string().regex(/^\d{4,6}$/, "PIN must be 4-6 digits"),
  confirmPin: z.string().regex(/^\d{4,6}$/, "PIN must be 4-6 digits")
}).refine((data) => data.pin === data.confirmPin, {
  message: "PINs don't match",
  path: ["confirmPin"]
});
var pinLoginSchema = z.object({
  identifier: z.string().min(1, "Mobile number or email is required"),
  pin: z.string().regex(/^\d{4,6}$/, "PIN must be 4-6 digits")
});
var biometricSetupSchema = z.object({
  enabled: z.boolean()
});
var insertOtpSchema = createInsertSchema(otpVerifications).omit({
  id: true,
  createdAt: true
});
var insertUserProfileSchema = createInsertSchema(userProfiles).omit({
  id: true,
  userId: true,
  createdAt: true,
  updatedAt: true
}).extend({
  name: z.string().min(1, "Name is required").regex(/^[a-zA-Z\s]+$/, "Name can only contain letters and spaces"),
  age: z.number().min(1, "Age must be greater than 0").max(120, "Age must be reasonable"),
  gender: z.string().optional(),
  address: z.string().min(1, "Address is required"),
  bloodGroup: z.string().min(1, "Blood group is required"),
  state: z.string().min(1, "State is required"),
  city: z.string().min(1, "City is required"),
  pinCode: z.string().min(1, "Pin code is required"),
  alternatePhone: z.string().optional().refine((val) => !val || /^[0-9+\-() ]*$/.test(val), {
    message: "Phone number can only contain numbers, +, -, (, ), and spaces"
  }),
  email: z.string().email("Please enter a valid email address").min(1, "Email address is required"),
  profilePicture: z.string().optional().nullable(),
  driversLicenseNumber: z.string().optional(),
  driversLicenseCopy: z.string().optional().nullable(),
  driversLicenseValidTill: z.string().optional().nullable()
});
var insertServiceAlertSchema = createInsertSchema(serviceAlerts).omit({
  id: true,
  createdAt: true,
  updatedAt: true
}).extend({
  eventName: z.string().min(1, "Event name is required"),
  scheduledDate: z.string().min(1, "Scheduled date is required"),
  notes: z.string().optional()
});
var notifications = pgTable("notifications", {
  id: serial("id").primaryKey(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id),
  type: text("type").notNull(),
  // 'insurance', 'emission', 'service'
  title: text("title").notNull(),
  message: text("message").notNull(),
  dueDate: date("due_date").notNull(),
  isRead: boolean("is_read").default(false),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var insertNotificationSchema = createInsertSchema(notifications).omit({
  id: true,
  createdAt: true
});
var emergencyContacts = pgTable("emergency_contacts", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").notNull(),
  // Emergency Contact
  emergencyName: text("emergency_name"),
  emergencyPhone: text("emergency_phone"),
  // Insurance Company
  insuranceName: text("insurance_name"),
  insurancePhone: text("insurance_phone"),
  // Roadside Assistance
  roadsidePhone: text("roadside_phone"),
  // Service Centre
  serviceCentreName: text("service_centre_name"),
  serviceCentrePhone: text("service_centre_phone"),
  // Spare Parts Provider
  sparePartsName: text("spare_parts_name"),
  sparePartsPhone: text("spare_parts_phone"),
  createdAt: timestamp("created_at").defaultNow(),
  updatedAt: timestamp("updated_at").defaultNow()
});
var insertEmergencyContactSchema = createInsertSchema(emergencyContacts).omit({
  id: true,
  userId: true,
  createdAt: true,
  updatedAt: true
});
var insertRatingSchema = createInsertSchema(ratings, {
  rating: z.number().min(1).max(5),
  userName: z.string().min(1, "Name is required"),
  phoneNumber: z.string().min(10, "Valid phone number is required"),
  emailId: z.string().email("Valid email is required"),
  feedback: z.string().optional()
}).omit({
  id: true,
  createdAt: true
});
var broadcasts = pgTable("broadcasts", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  type: text("type").notNull(),
  // "sell", "buy", "query"
  title: text("title").notNull(),
  description: text("description").notNull(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id),
  // Only for sell type
  price: integer("price"),
  // For sell/buy posts, in rupees
  contactPhone: text("contact_phone").notNull(),
  contactEmail: text("contact_email"),
  location: text("location"),
  // City/area
  status: text("status").default("active").notNull(),
  // "active", "sold", "resolved", "expired"
  viewCount: integer("view_count").default(0).notNull(),
  isActive: boolean("is_active").default(true).notNull(),
  expiresAt: timestamp("expires_at"),
  // Auto-expire posts after certain time
  createdAt: timestamp("created_at").defaultNow().notNull(),
  updatedAt: timestamp("updated_at").defaultNow().notNull()
});
var broadcastResponses = pgTable("broadcast_responses", {
  id: serial("id").primaryKey(),
  broadcastId: integer("broadcast_id").references(() => broadcasts.id).notNull(),
  userId: integer("user_id").references(() => users.id).notNull(),
  message: text("message").notNull(),
  contactPhone: text("contact_phone"),
  contactEmail: text("contact_email"),
  isRead: boolean("is_read").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var insertBroadcastSchema = createInsertSchema(broadcasts).omit({
  id: true,
  userId: true,
  viewCount: true,
  createdAt: true,
  updatedAt: true
}).extend({
  title: z.string().min(1, "Title is required").max(100, "Title must be less than 100 characters"),
  description: z.string().min(1, "Description is required").max(500, "Description must be less than 500 characters"),
  price: z.number().optional().nullable(),
  contactPhone: z.string().min(10, "Valid contact phone is required"),
  contactEmail: z.string().email("Valid email is required").optional(),
  location: z.string().optional(),
  expiresAt: z.string().optional().nullable()
});
var insertBroadcastResponseSchema = createInsertSchema(broadcastResponses).omit({
  id: true,
  userId: true,
  createdAt: true
}).extend({
  message: z.string().min(1, "Message is required").max(300, "Message must be less than 300 characters"),
  contactPhone: z.string().min(10, "Valid contact phone is required").optional(),
  contactEmail: z.string().email("Valid email is required").optional()
});
var trafficViolations = pgTable("traffic_violations", {
  id: serial("id").primaryKey(),
  vehicleId: integer("vehicle_id").references(() => vehicles.id).notNull(),
  challanNumber: text("challan_number").notNull(),
  offense: text("offense").notNull(),
  fineAmount: integer("fine_amount").notNull(),
  violationDate: date("violation_date").notNull(),
  location: text("location").notNull(),
  status: text("status").notNull(),
  // 'paid', 'unpaid', 'pending'
  paymentDate: date("payment_date"),
  lastChecked: timestamp("last_checked").defaultNow().notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var insertTrafficViolationSchema = createInsertSchema(trafficViolations).omit({
  id: true,
  createdAt: true
});
var insertCalendarReminderSchema = createInsertSchema(calendarReminders).omit({
  id: true,
  userId: true,
  notificationSent: true,
  isCompleted: true,
  createdAt: true,
  updatedAt: true
}).extend({
  title: z.string().min(1, "Title is required").max(100, "Title must be less than 100 characters"),
  details: z.string().min(1, "Details are required").max(500, "Details must be less than 500 characters"),
  reminderDate: z.string().min(1, "Reminder date is required")
});
var insertSubscriptionSchema = createInsertSchema(subscriptions).omit({
  id: true,
  createdAt: true,
  updatedAt: true
});
var insertPaymentHistorySchema = createInsertSchema(paymentHistory).omit({
  id: true,
  createdAt: true
});
var insertSubscriptionNotificationSchema = createInsertSchema(subscriptionNotifications).omit({
  id: true,
  createdAt: true
});
var insertAccountInformationSchema = createInsertSchema(accountInformation).omit({
  id: true,
  createdAt: true,
  updatedAt: true
}).extend({
  userId: z.number().min(1, "User ID is required"),
  accountType: z.string().default("individual"),
  subscriptionPlan: z.string().default("premium"),
  accountStatus: z.string().default("active"),
  billingAddress: z.string().optional(),
  gstNumber: z.string().optional(),
  panNumber: z.string().optional(),
  preferredPaymentMethod: z.string().optional(),
  accountNotes: z.string().optional()
});
var insertInvoiceSchema = createInsertSchema(invoices).omit({
  id: true,
  createdAt: true,
  updatedAt: true
}).extend({
  userId: z.number().min(1, "User ID is required"),
  invoiceNumber: z.string().min(1, "Invoice number is required"),
  amount: z.number().min(1, "Amount must be greater than 0"),
  totalAmount: z.number().min(1, "Total amount must be greater than 0"),
  customerName: z.string().min(1, "Customer name is required"),
  customerPhone: z.string().min(10, "Valid phone number is required"),
  description: z.string().min(1, "Description is required"),
  billingPeriodStart: z.string().min(1, "Billing period start is required"),
  billingPeriodEnd: z.string().min(1, "Billing period end is required"),
  dueDate: z.string().min(1, "Due date is required")
});
var insertNewsItemSchema = createInsertSchema(newsItems).omit({
  id: true,
  createdAt: true,
  lastUpdated: true
});
var insertNewsUpdateLogSchema = createInsertSchema(newsUpdateLog).omit({
  id: true,
  lastUpdated: true
});
var insertDashboardWidgetSchema = createInsertSchema(dashboardWidgets).omit({
  id: true,
  createdAt: true,
  updatedAt: true
});
var adminMessages = pgTable("admin_messages", {
  id: serial("id").primaryKey(),
  message: text("message").notNull(),
  messageDate: date("message_date").notNull(),
  isActive: boolean("is_active").default(true),
  createdAt: timestamp("created_at").defaultNow(),
  updatedAt: timestamp("updated_at").defaultNow()
});
var insertAdminMessageSchema = createInsertSchema(adminMessages).omit({
  id: true,
  createdAt: true,
  updatedAt: true
});
var userMessages = pgTable("user_messages", {
  id: serial("id").primaryKey(),
  userId: integer("user_id").references(() => users.id).notNull(),
  message: text("message").notNull(),
  fromAdmin: boolean("from_admin").default(true).notNull(),
  isRead: boolean("is_read").default(false).notNull(),
  isDismissed: boolean("is_dismissed").default(false).notNull(),
  createdAt: timestamp("created_at").defaultNow().notNull()
});
var insertUserMessageSchema = createInsertSchema(userMessages).omit({
  id: true,
  createdAt: true
});

// server/db.ts
import { Pool, neonConfig } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-serverless";
import ws from "ws";
neonConfig.webSocketConstructor = ws;
if (!process.env.DATABASE_URL) {
  throw new Error(
    "DATABASE_URL must be set. Did you forget to provision a database?"
  );
}
var pool = new Pool({ connectionString: process.env.DATABASE_URL });
var db = drizzle({ client: pool, schema: schema_exports });

// server/storage.ts
import { eq, and, gt, lte, lt, desc, gte, or, inArray, sql } from "drizzle-orm";
var DatabaseStorage = class {
  async getUser(id) {
    const [user] = await db.select().from(users).where(eq(users.id, id));
    return user || void 0;
  }
  async getUserByUsername(username) {
    const [user] = await db.select().from(users).where(eq(users.username, username));
    return user || void 0;
  }
  async getUserByIdentifier(identifier) {
    const [user] = await db.select().from(users).where(
      or(
        eq(users.username, identifier),
        eq(users.mobile, identifier),
        eq(users.email, identifier)
      )
    );
    return user || void 0;
  }
  async createUser(insertUser) {
    const [user] = await db.insert(users).values(insertUser).returning();
    return user;
  }
  async updateUser(id, userUpdate) {
    const [user] = await db.update(users).set(userUpdate).where(eq(users.id, id)).returning();
    return user || void 0;
  }
  async verifyPin(identifier, pin) {
    const [user] = await db.select().from(users).where(
      and(
        or(
          eq(users.username, identifier),
          eq(users.mobile, identifier),
          eq(users.email, identifier)
        ),
        eq(users.pin, pin)
      )
    );
    return user || void 0;
  }
  async updateUserPin(id, pin) {
    const [user] = await db.update(users).set({ pin }).where(eq(users.id, id)).returning();
    return user || void 0;
  }
  async updateBiometricSetting(id, enabled) {
    const [user] = await db.update(users).set({ biometricEnabled: enabled }).where(eq(users.id, id)).returning();
    return user || void 0;
  }
  async createOtpVerification(insertOtp) {
    const [otp] = await db.insert(otpVerifications).values(insertOtp).returning();
    return otp;
  }
  async getValidOtp(identifier, otpCode) {
    const [otp] = await db.select().from(otpVerifications).where(
      and(
        eq(otpVerifications.identifier, identifier),
        eq(otpVerifications.otp, otpCode),
        eq(otpVerifications.isUsed, false),
        gt(otpVerifications.expiresAt, /* @__PURE__ */ new Date())
      )
    );
    return otp || void 0;
  }
  async getLatestOtp(identifier) {
    const [otp] = await db.select().from(otpVerifications).where(eq(otpVerifications.identifier, identifier)).orderBy(desc(otpVerifications.createdAt)).limit(1);
    return otp || void 0;
  }
  async markOtpAsUsed(id) {
    await db.update(otpVerifications).set({ isUsed: true }).where(eq(otpVerifications.id, id));
  }
  async getUserProfile(userId) {
    const [profile] = await db.select().from(userProfiles).where(eq(userProfiles.userId, userId));
    return profile || void 0;
  }
  async createUserProfile(insertProfile) {
    const [profile] = await db.insert(userProfiles).values(insertProfile).returning();
    return profile;
  }
  async updateUserProfile(userId, profileUpdate) {
    const [profile] = await db.update(userProfiles).set(profileUpdate).where(eq(userProfiles.userId, userId)).returning();
    return profile || void 0;
  }
  async getVehicles(userId) {
    return await db.select().from(vehicles).where(eq(vehicles.userId, userId)).orderBy(vehicles.createdAt);
  }
  async getVehicle(id, userId) {
    const [vehicle] = await db.select().from(vehicles).where(and(eq(vehicles.id, id), eq(vehicles.userId, userId)));
    return vehicle || void 0;
  }
  async createVehicle(insertVehicle) {
    const [vehicle] = await db.insert(vehicles).values(insertVehicle).returning();
    return vehicle;
  }
  async updateVehicle(id, userId, vehicleUpdate) {
    const [vehicle] = await db.update(vehicles).set(vehicleUpdate).where(and(eq(vehicles.id, id), eq(vehicles.userId, userId))).returning();
    return vehicle || void 0;
  }
  async updateVehicleSpeedometerReading(vehicleId, speedometerReading) {
    const [vehicle] = await db.update(vehicles).set({ speedometerReading }).where(eq(vehicles.id, vehicleId)).returning();
    return vehicle || void 0;
  }
  async deleteVehicle(id, userId) {
    try {
      console.log(`Attempting to delete vehicle ${id} and related records`);
      const notificationsDeleted = await db.delete(notifications).where(eq(notifications.vehicleId, id));
      console.log(`Deleted ${notificationsDeleted.rowCount || 0} notifications`);
      const serviceAlertsDeleted = await db.delete(serviceAlerts).where(eq(serviceAlerts.vehicleId, id));
      console.log(`Deleted ${serviceAlertsDeleted.rowCount || 0} service alerts`);
      const broadcastsDeleted = await db.delete(broadcasts).where(eq(broadcasts.vehicleId, id));
      console.log(`Deleted ${broadcastsDeleted.rowCount || 0} broadcasts`);
      const maintenanceDeleted = await db.delete(maintenanceRecords).where(eq(maintenanceRecords.vehicleId, id));
      console.log(`Deleted ${maintenanceDeleted.rowCount || 0} maintenance records`);
      const serviceLogsDeleted = await db.delete(serviceLogs).where(eq(serviceLogs.vehicleId, id));
      console.log(`Deleted ${serviceLogsDeleted.rowCount || 0} service logs`);
      const violationsDeleted = await db.delete(trafficViolations).where(eq(trafficViolations.vehicleId, id));
      console.log(`Deleted ${violationsDeleted.rowCount || 0} traffic violations`);
      const documentsDeleted = await db.delete(documents).where(eq(documents.vehicleId, id));
      console.log(`Deleted ${documentsDeleted.rowCount || 0} documents`);
      const documentExpiriesDeleted = await db.delete(documentExpiries).where(eq(documentExpiries.vehicleId, id));
      console.log(`Deleted ${documentExpiriesDeleted.rowCount || 0} document expiries`);
      const result = await db.delete(vehicles).where(and(eq(vehicles.id, id), eq(vehicles.userId, userId)));
      console.log(`Deleted ${result.rowCount || 0} vehicles`);
      return (result.rowCount || 0) > 0;
    } catch (error) {
      console.error("Error deleting vehicle:", error);
      throw error;
    }
  }
  async getDocumentsByVehicle(vehicleId) {
    return await db.select().from(documents).where(eq(documents.vehicleId, vehicleId));
  }
  async getDocument(id) {
    const [document] = await db.select().from(documents).where(eq(documents.id, id));
    return document || void 0;
  }
  async createDocument(insertDocument) {
    const [document] = await db.insert(documents).values(insertDocument).returning();
    return document;
  }
  async deleteDocument(id) {
    const result = await db.delete(documents).where(eq(documents.id, id));
    return (result.rowCount || 0) > 0;
  }
  async getNotifications(userId) {
    const userVehicles = await this.getVehicles(userId);
    const vehicleIds = userVehicles.map((v) => v.id);
    if (vehicleIds.length === 0) {
      return [];
    }
    return await db.select().from(notifications).where(inArray(notifications.vehicleId, vehicleIds)).orderBy(notifications.createdAt);
  }
  async getNotificationsByVehicle(vehicleId) {
    return await db.select().from(notifications).where(eq(notifications.vehicleId, vehicleId)).orderBy(notifications.createdAt);
  }
  async createNotification(notification) {
    const [newNotification] = await db.insert(notifications).values(notification).returning();
    return newNotification;
  }
  async markNotificationAsRead(id) {
    await db.update(notifications).set({ isRead: true }).where(eq(notifications.id, id));
  }
  async generateRenewalNotifications(userId) {
    const allVehicles = await this.getVehicles(userId);
    const today = /* @__PURE__ */ new Date();
    for (const vehicle of allVehicles) {
      if (vehicle.insuranceExpiry) {
        const insuranceIssuanceDate = new Date(vehicle.insuranceExpiry);
        const tenMonthsFromIssuance = new Date(insuranceIssuanceDate);
        tenMonthsFromIssuance.setMonth(tenMonthsFromIssuance.getMonth() + 10);
        if (today >= tenMonthsFromIssuance) {
          const existingNotifications = await db.select().from(notifications).where(and(
            eq(notifications.vehicleId, vehicle.id),
            eq(notifications.type, "insurance"),
            eq(notifications.dueDate, vehicle.insuranceExpiry)
          ));
          if (existingNotifications.length === 0) {
            await this.createNotification({
              vehicleId: vehicle.id,
              type: "insurance",
              title: "Insurance Renewal Due",
              message: `Insurance for your ${vehicle.make} ${vehicle.model} was issued on ${insuranceIssuanceDate.toLocaleDateString()}. Time to renew!`,
              dueDate: vehicle.insuranceExpiry,
              isRead: false
            });
          }
        }
      }
      if (vehicle.emissionExpiry) {
        const emissionDate = new Date(vehicle.emissionExpiry);
        const fiveMonthsFromEmission = new Date(emissionDate);
        fiveMonthsFromEmission.setMonth(fiveMonthsFromEmission.getMonth() + 5);
        if (today >= fiveMonthsFromEmission) {
          const existingNotifications = await db.select().from(notifications).where(and(
            eq(notifications.vehicleId, vehicle.id),
            eq(notifications.type, "emission"),
            eq(notifications.dueDate, vehicle.emissionExpiry)
          ));
          if (existingNotifications.length === 0) {
            await this.createNotification({
              vehicleId: vehicle.id,
              type: "emission",
              title: "Emission Certificate Renewal Due",
              message: `Latest emission certificate for your ${vehicle.make} ${vehicle.model} was from ${emissionDate.toLocaleDateString()}. Time to renew!`,
              dueDate: vehicle.emissionExpiry,
              isRead: false
            });
          }
        }
      }
      if (vehicle.rcExpiry) {
        const rcExpiryDate = new Date(vehicle.rcExpiry);
        const fourMonthsBeforeExpiry = new Date(rcExpiryDate);
        fourMonthsBeforeExpiry.setMonth(fourMonthsBeforeExpiry.getMonth() - 4);
        if (today >= fourMonthsBeforeExpiry && today < rcExpiryDate) {
          const existingNotifications = await db.select().from(notifications).where(and(
            eq(notifications.vehicleId, vehicle.id),
            eq(notifications.type, "rc"),
            eq(notifications.dueDate, vehicle.rcExpiry)
          ));
          if (existingNotifications.length === 0) {
            await this.createNotification({
              vehicleId: vehicle.id,
              type: "rc",
              title: "RC Renewal Due",
              message: `Your RC expires on ${rcExpiryDate.toLocaleDateString()}. Renew it before expiry to avoid penalties.`,
              dueDate: vehicle.rcExpiry,
              isRead: false
            });
          }
        }
      }
    }
  }
  async getEmergencyContact(userId) {
    const [contact] = await db.select().from(emergencyContacts).where(eq(emergencyContacts.userId, userId));
    return contact || void 0;
  }
  async createEmergencyContact(insertContact) {
    const [contact] = await db.insert(emergencyContacts).values(insertContact).returning();
    return contact;
  }
  async updateEmergencyContact(userId, contactUpdate) {
    const [contact] = await db.update(emergencyContacts).set({ ...contactUpdate, updatedAt: /* @__PURE__ */ new Date() }).where(eq(emergencyContacts.userId, userId)).returning();
    return contact || void 0;
  }
  // Traffic Violation methods
  async getTrafficViolations(vehicleId) {
    return await db.select().from(trafficViolations).where(eq(trafficViolations.vehicleId, vehicleId)).orderBy(trafficViolations.violationDate);
  }
  async createTrafficViolation(violation) {
    const [newViolation] = await db.insert(trafficViolations).values(violation).returning();
    return newViolation;
  }
  async updateTrafficViolationStatus(violationId, status, paymentDate) {
    const [updated] = await db.update(trafficViolations).set({
      status,
      paymentDate: paymentDate ? new Date(paymentDate) : null,
      lastChecked: /* @__PURE__ */ new Date()
    }).where(eq(trafficViolations.id, violationId)).returning();
    return updated;
  }
  async deleteTrafficViolation(violationId) {
    const result = await db.delete(trafficViolations).where(eq(trafficViolations.id, violationId));
    return result.rowCount !== null && result.rowCount > 0;
  }
  async clearVehicleViolations(vehicleId) {
    await db.delete(trafficViolations).where(eq(trafficViolations.vehicleId, vehicleId));
  }
  // Maintenance Schedule methods
  async getMaintenanceSchedule(make, model, year, drivingCondition) {
    const [schedule] = await db.select().from(maintenanceSchedules).where(
      and(
        eq(maintenanceSchedules.make, make),
        eq(maintenanceSchedules.model, model),
        eq(maintenanceSchedules.year, year),
        eq(maintenanceSchedules.drivingCondition, drivingCondition)
      )
    ).orderBy(maintenanceSchedules.lastUpdated).limit(1);
    return schedule;
  }
  async createMaintenanceSchedule(schedule) {
    const [newSchedule] = await db.insert(maintenanceSchedules).values(schedule).returning();
    return newSchedule;
  }
  async updateMaintenanceSchedule(id, scheduleUpdate) {
    const [updated] = await db.update(maintenanceSchedules).set({ ...scheduleUpdate, lastUpdated: /* @__PURE__ */ new Date() }).where(eq(maintenanceSchedules.id, id)).returning();
    return updated;
  }
  // Maintenance Record methods
  async getMaintenanceRecords(vehicleId) {
    return await db.select().from(maintenanceRecords).where(eq(maintenanceRecords.vehicleId, vehicleId)).orderBy(desc(maintenanceRecords.completedDate));
  }
  async getMaintenanceRecord(id) {
    const [record] = await db.select().from(maintenanceRecords).where(eq(maintenanceRecords.id, id));
    return record;
  }
  async createMaintenanceRecord(record) {
    const [newRecord] = await db.insert(maintenanceRecords).values({
      vehicleId: record.vehicleId,
      maintenanceType: record.maintenanceType,
      completedDate: record.completedDate,
      warrantyCardPath: record.warrantyCardPath,
      invoicePath: record.invoicePath,
      notes: record.notes
    }).returning();
    return newRecord;
  }
  async updateMaintenanceRecord(id, recordUpdate) {
    const [updated] = await db.update(maintenanceRecords).set({ ...recordUpdate, updatedAt: /* @__PURE__ */ new Date() }).where(eq(maintenanceRecords.id, id)).returning();
    return updated;
  }
  async deleteMaintenanceRecord(id) {
    const result = await db.delete(maintenanceRecords).where(eq(maintenanceRecords.id, id));
    return (result.rowCount ?? 0) > 0;
  }
  // Service Log methods
  async getServiceLogs(vehicleId) {
    return await db.select().from(serviceLogs).where(eq(serviceLogs.vehicleId, vehicleId)).orderBy(desc(serviceLogs.serviceDate));
  }
  async getServiceLog(id) {
    const [serviceLog] = await db.select().from(serviceLogs).where(eq(serviceLogs.id, id));
    return serviceLog;
  }
  async createServiceLog(serviceLog) {
    const [newLog] = await db.insert(serviceLogs).values(serviceLog).returning();
    return newLog;
  }
  async updateServiceLog(id, serviceLogUpdate) {
    const [updated] = await db.update(serviceLogs).set({ ...serviceLogUpdate, updatedAt: /* @__PURE__ */ new Date() }).where(eq(serviceLogs.id, id)).returning();
    return updated;
  }
  async deleteServiceLog(id) {
    const result = await db.delete(serviceLogs).where(eq(serviceLogs.id, id));
    return (result.rowCount ?? 0) > 0;
  }
  // News methods
  async getNewsItems() {
    const items = await db.select().from(newsItems).orderBy(desc(newsItems.createdAt));
    return items;
  }
  async createNewsItem(newsItem) {
    const [item] = await db.insert(newsItems).values(newsItem).returning();
    return item;
  }
  async updateNewsItem(id, newsItemUpdate) {
    const [updated] = await db.update(newsItems).set({ ...newsItemUpdate, lastUpdated: /* @__PURE__ */ new Date() }).where(eq(newsItems.id, id)).returning();
    return updated;
  }
  async deleteNewsItem(id) {
    const result = await db.delete(newsItems).where(eq(newsItems.id, id));
    return (result.rowCount ?? 0) > 0;
  }
  async clearAllNews() {
    await db.delete(newsItems);
  }
  async createNewsUpdateLog(log2) {
    const [logEntry] = await db.insert(newsUpdateLog).values(log2).returning();
    return logEntry;
  }
  async getLastNewsUpdate() {
    const [lastUpdate] = await db.select().from(newsUpdateLog).orderBy(desc(newsUpdateLog.lastUpdated)).limit(1);
    return lastUpdate;
  }
  // Dashboard Widget methods
  async getDashboardWidgets(userId) {
    return await db.select().from(dashboardWidgets).where(eq(dashboardWidgets.userId, userId)).orderBy(dashboardWidgets.position);
  }
  async createDashboardWidget(widget) {
    const [newWidget] = await db.insert(dashboardWidgets).values(widget).returning();
    return newWidget;
  }
  async updateDashboardWidget(id, updates) {
    const [updatedWidget] = await db.update(dashboardWidgets).set({ ...updates, updatedAt: /* @__PURE__ */ new Date() }).where(eq(dashboardWidgets.id, id)).returning();
    return updatedWidget;
  }
  async deleteDashboardWidget(id) {
    await db.delete(dashboardWidgets).where(eq(dashboardWidgets.id, id));
  }
  async initializeDefaultWidgets(userId) {
    const defaultWidgets = [
      { userId, widgetType: "stats", position: 0, isVisible: true, size: "medium" },
      { userId, widgetType: "quick_actions", position: 1, isVisible: true, size: "medium" },
      { userId, widgetType: "recent_vehicles", position: 2, isVisible: true, size: "large" },
      { userId, widgetType: "news", position: 3, isVisible: true, size: "medium" },
      { userId, widgetType: "reminders", position: 4, isVisible: true, size: "small" }
    ];
    const createdWidgets = await db.insert(dashboardWidgets).values(defaultWidgets).returning();
    return createdWidgets;
  }
  // Rating methods
  async createRating(rating) {
    const [newRating] = await db.insert(ratings).values(rating).returning();
    return newRating;
  }
  async getRatings() {
    return await db.select().from(ratings).orderBy(desc(ratings.createdAt));
  }
  async getUserRatings(userId) {
    return await db.select().from(ratings).where(eq(ratings.userId, userId)).orderBy(desc(ratings.createdAt));
  }
  // Service Alert methods
  async getServiceAlerts(vehicleId) {
    const alerts = await db.select().from(serviceAlerts).where(eq(serviceAlerts.vehicleId, vehicleId)).orderBy(desc(serviceAlerts.scheduledDate));
    return alerts;
  }
  async getServiceAlert(id) {
    const [alert] = await db.select().from(serviceAlerts).where(eq(serviceAlerts.id, id));
    return alert || void 0;
  }
  async createServiceAlert(serviceAlert) {
    const [alert] = await db.insert(serviceAlerts).values(serviceAlert).returning();
    return alert;
  }
  async updateServiceAlert(id, serviceAlert) {
    const [alert] = await db.update(serviceAlerts).set({ ...serviceAlert, updatedAt: /* @__PURE__ */ new Date() }).where(eq(serviceAlerts.id, id)).returning();
    return alert || void 0;
  }
  async deleteServiceAlert(id) {
    const [result] = await db.delete(serviceAlerts).where(eq(serviceAlerts.id, id)).returning({ id: serviceAlerts.id });
    return !!result;
  }
  async getActiveServiceAlerts() {
    const alerts = await db.select().from(serviceAlerts).where(and(eq(serviceAlerts.isActive, true), eq(serviceAlerts.isCompleted, false))).orderBy(serviceAlerts.scheduledDate);
    return alerts;
  }
  // Broadcast methods
  async getBroadcasts() {
    const broadcastList = await db.select().from(broadcasts).leftJoin(vehicles, eq(broadcasts.vehicleId, vehicles.id)).where(eq(broadcasts.isActive, true)).orderBy(desc(broadcasts.createdAt));
    return broadcastList.map((row) => ({
      ...row.broadcasts,
      vehicle: row.vehicles || void 0
    }));
  }
  async getBroadcast(id) {
    const [broadcast] = await db.select().from(broadcasts).where(eq(broadcasts.id, id));
    return broadcast;
  }
  async createBroadcast(broadcastData) {
    const [broadcast] = await db.insert(broadcasts).values(broadcastData).returning();
    return broadcast;
  }
  async updateBroadcast(id, broadcastUpdate) {
    const updateData = { ...broadcastUpdate };
    if (updateData.expiresAt && typeof updateData.expiresAt === "string") {
      updateData.expiresAt = new Date(updateData.expiresAt);
    }
    const [updated] = await db.update(broadcasts).set({ ...updateData, updatedAt: /* @__PURE__ */ new Date() }).where(eq(broadcasts.id, id)).returning();
    return updated;
  }
  async deleteBroadcast(id, userId) {
    const result = await db.delete(broadcasts).where(and(eq(broadcasts.id, id), eq(broadcasts.userId, userId)));
    return (result.rowCount ?? 0) > 0;
  }
  // Admin method to delete any post
  async adminDeleteBroadcast(id) {
    const result = await db.delete(broadcasts).where(eq(broadcasts.id, id));
    return (result.rowCount ?? 0) > 0;
  }
  async cleanupExpiredBroadcasts() {
    const now = /* @__PURE__ */ new Date();
    const result = await db.delete(broadcasts).where(lt(broadcasts.expiresAt, now));
    const deletedCount = result.rowCount ?? 0;
    if (deletedCount > 0) {
      console.log(`Cleaned up ${deletedCount} expired broadcasts`);
    }
    return deletedCount;
  }
  async getBroadcastsByType(type) {
    return await db.select().from(broadcasts).where(and(eq(broadcasts.type, type), eq(broadcasts.isActive, true))).orderBy(desc(broadcasts.createdAt));
  }
  async getUserActiveBroadcastCount(userId) {
    const result = await db.select({ count: sql`count(*)` }).from(broadcasts).where(and(
      eq(broadcasts.userId, userId),
      eq(broadcasts.isActive, true),
      eq(broadcasts.status, "active")
    ));
    return result[0]?.count || 0;
  }
  async incrementBroadcastViews(id) {
    const [broadcast] = await db.select({ viewCount: broadcasts.viewCount }).from(broadcasts).where(eq(broadcasts.id, id));
    if (broadcast) {
      await db.update(broadcasts).set({ viewCount: (broadcast.viewCount || 0) + 1 }).where(eq(broadcasts.id, id));
    }
  }
  // Broadcast Response methods
  async getBroadcastResponses(broadcastId) {
    return await db.select().from(broadcastResponses).where(eq(broadcastResponses.broadcastId, broadcastId)).orderBy(desc(broadcastResponses.createdAt));
  }
  async createBroadcastResponse(responseData) {
    const [response] = await db.insert(broadcastResponses).values(responseData).returning();
    return response;
  }
  async markResponseAsRead(id) {
    await db.update(broadcastResponses).set({ isRead: true }).where(eq(broadcastResponses.id, id));
  }
  // Admin methods implementation
  async getAdminStats() {
    const [
      totalUsersResult,
      totalVehiclesResult,
      totalBroadcastsResult,
      totalRatingsResult,
      subscriptionRevenueResult,
      newUsersThisMonthResult,
      newVehiclesThisMonthResult,
      userProfilesResult
    ] = await Promise.all([
      db.select().from(users),
      db.select().from(vehicles),
      db.select().from(broadcasts),
      db.select().from(ratings),
      db.select().from(users).where(eq(users.subscriptionStatus, "paid")),
      db.select().from(users).where(gte(users.createdAt, new Date((/* @__PURE__ */ new Date()).setDate(1)))),
      db.select().from(vehicles).where(gte(vehicles.createdAt, new Date((/* @__PURE__ */ new Date()).setDate(1)))),
      db.select().from(userProfiles)
    ]);
    const maleUsers = userProfilesResult.filter((p) => p.gender === "male").length;
    const femaleUsers = userProfilesResult.filter((p) => p.gender === "female").length;
    const twoWheelers = totalVehiclesResult.filter((v) => v.vehicleType === "2-wheeler").length;
    const threeWheelers = totalVehiclesResult.filter((v) => v.vehicleType === "3-wheeler").length;
    const fourWheelers = totalVehiclesResult.filter((v) => v.vehicleType === "4-wheeler").length;
    const dailyActiveUsers = Math.floor(totalUsersResult.filter((u) => u.isVerified).length * 0.3);
    const monthlyActiveUsers = Math.floor(totalUsersResult.filter((u) => u.isVerified).length * 0.8);
    const usersByState = {};
    userProfilesResult.forEach((profile) => {
      if (profile.state) {
        usersByState[profile.state] = (usersByState[profile.state] || 0) + 1;
      }
    });
    return {
      totalUsers: totalUsersResult.length,
      activeUsers: totalUsersResult.filter((u) => u.isVerified).length,
      dailyActiveUsers,
      monthlyActiveUsers,
      maleUsers,
      femaleUsers,
      usersByState,
      totalVehicles: totalVehiclesResult.length,
      twoWheelers,
      threeWheelers,
      fourWheelers,
      totalBroadcasts: totalBroadcastsResult.length,
      totalRatings: totalRatingsResult.length,
      subscriptionRevenue: subscriptionRevenueResult.length * 100,
      // ₹100 per subscription
      newUsersThisMonth: newUsersThisMonthResult.length,
      newVehiclesThisMonth: newVehiclesThisMonthResult.length
    };
  }
  async getRecentUsers(limit = 10) {
    return await db.select({
      id: users.id,
      username: users.username,
      password: users.password,
      email: users.email,
      mobile: users.mobile,
      isVerified: users.isVerified,
      pin: users.pin,
      biometricEnabled: users.biometricEnabled,
      subscriptionStatus: users.subscriptionStatus,
      subscriptionExpiry: users.subscriptionExpiry,
      razorpayOrderId: users.razorpayOrderId,
      razorpayPaymentId: users.razorpayPaymentId,
      isAdmin: users.isAdmin,
      adminRole: users.adminRole,
      isBlocked: users.isBlocked,
      blockedAt: users.blockedAt,
      blockedReason: users.blockedReason,
      createdAt: users.createdAt,
      // Add user profile information
      name: userProfiles.name,
      profileEmail: userProfiles.email
    }).from(users).leftJoin(userProfiles, eq(users.id, userProfiles.userId)).orderBy(desc(users.createdAt)).limit(limit);
  }
  async getRecentVehicles(limit = 10) {
    return await db.select().from(vehicles).orderBy(desc(vehicles.createdAt)).limit(limit);
  }
  async getRecentBroadcasts(limit = 10) {
    return await db.select().from(broadcasts).orderBy(desc(broadcasts.createdAt)).limit(limit);
  }
  async getRecentRatings(limit = 10) {
    return await db.select().from(ratings).orderBy(desc(ratings.createdAt)).limit(limit);
  }
  async getAllUsersData() {
    return await db.select().from(users).orderBy(desc(users.createdAt));
  }
  // Admin User Management Methods
  async blockUser(userId, reason) {
    await db.update(users).set({
      isBlocked: true,
      blockedAt: /* @__PURE__ */ new Date(),
      blockedReason: reason
    }).where(eq(users.id, userId));
  }
  async unblockUser(userId) {
    await db.update(users).set({
      isBlocked: false,
      blockedAt: null,
      blockedReason: null
    }).where(eq(users.id, userId));
  }
  async deleteUser(userId) {
    try {
      const userExists = await this.getUser(userId);
      if (!userExists) {
        return false;
      }
      const userVehicles = await this.getVehicles(userId);
      const vehicleIds = userVehicles.map((v) => v.id);
      if (vehicleIds.length > 0) {
        await db.delete(notifications).where(inArray(notifications.vehicleId, vehicleIds));
        await db.delete(serviceLogs).where(inArray(serviceLogs.vehicleId, vehicleIds));
        await db.delete(maintenanceRecords).where(inArray(maintenanceRecords.vehicleId, vehicleIds));
        await db.delete(trafficViolations).where(inArray(trafficViolations.vehicleId, vehicleIds));
        await db.delete(documentExpiries).where(inArray(documentExpiries.vehicleId, vehicleIds));
        await db.delete(documents).where(inArray(documents.vehicleId, vehicleIds));
        await db.delete(vehicles).where(eq(vehicles.userId, userId));
      }
      await db.delete(calendarReminders).where(eq(calendarReminders.userId, userId));
      await db.delete(emergencyContacts).where(eq(emergencyContacts.userId, userId));
      await db.delete(otpVerifications).where(eq(otpVerifications.identifier, userExists.mobile || userExists.username));
      await db.delete(ratings).where(eq(ratings.userId, userId));
      await db.delete(broadcasts).where(eq(broadcasts.userId, userId));
      await db.delete(broadcastResponses).where(eq(broadcastResponses.userId, userId));
      await db.delete(accountInformation).where(eq(accountInformation.userId, userId));
      await db.delete(invoices).where(eq(invoices.userId, userId));
      await db.delete(userProfiles).where(eq(userProfiles.userId, userId));
      const result = await db.delete(users).where(eq(users.id, userId));
      return (result.rowCount ?? 0) > 0;
    } catch (error) {
      console.error("Error deleting user:", error);
      return false;
    }
  }
  async getAllVehiclesData() {
    try {
      const allVehicles = await db.select().from(vehicles).orderBy(desc(vehicles.createdAt));
      const enhancedVehicles = await Promise.all(
        allVehicles.map(async (vehicle) => {
          const vehicleDocuments = await this.getDocumentsByVehicle(vehicle.id);
          const documentsByType = vehicleDocuments.reduce((acc, doc) => {
            if (!acc[doc.type]) {
              acc[doc.type] = [];
            }
            acc[doc.type].push({
              id: doc.id,
              fileName: doc.fileName,
              filePath: doc.filePath,
              uploadDate: doc.createdAt,
              expiryDate: doc.expiryDate,
              issueDate: doc.issueDate,
              documentNumber: doc.documentNumber,
              issuer: doc.issuer,
              amount: doc.amount
            });
            return acc;
          }, {});
          const latestInsurance = documentsByType.insurance?.[0] || null;
          const latestRC = documentsByType.rc?.[0] || null;
          const latestEmission = documentsByType.emission?.[0] || null;
          return {
            ...vehicle,
            // Document summary for quick reference
            latestInsuranceDetails: latestInsurance ? {
              fileName: latestInsurance.fileName,
              expiryDate: latestInsurance.expiryDate || vehicle.insuranceExpiry,
              issueDate: latestInsurance.issueDate,
              documentNumber: latestInsurance.documentNumber,
              issuer: latestInsurance.issuer,
              amount: latestInsurance.amount,
              uploadDate: latestInsurance.uploadDate
            } : null,
            latestRCDetails: latestRC ? {
              fileName: latestRC.fileName,
              expiryDate: latestRC.expiryDate || vehicle.rcExpiry,
              issueDate: latestRC.issueDate,
              documentNumber: latestRC.documentNumber,
              issuer: latestRC.issuer,
              uploadDate: latestRC.uploadDate
            } : null,
            latestEmissionDetails: latestEmission ? {
              fileName: latestEmission.fileName,
              expiryDate: latestEmission.expiryDate || vehicle.emissionExpiry,
              issueDate: latestEmission.issueDate,
              documentNumber: latestEmission.documentNumber,
              issuer: latestEmission.issuer,
              uploadDate: latestEmission.uploadDate
            } : null,
            // All documents organized by type
            allDocuments: documentsByType,
            // Document counts
            documentCounts: {
              insurance: documentsByType.insurance?.length || 0,
              rc: documentsByType.rc?.length || 0,
              emission: documentsByType.emission?.length || 0,
              service: documentsByType.service?.length || 0,
              other: Object.keys(documentsByType).filter(
                (type) => !["insurance", "rc", "emission", "service"].includes(type)
              ).reduce((sum, type) => sum + documentsByType[type].length, 0),
              total: vehicleDocuments.length
            }
          };
        })
      );
      return enhancedVehicles;
    } catch (error) {
      console.error("Error fetching enhanced vehicles data:", error);
      return await db.select().from(vehicles).orderBy(desc(vehicles.createdAt));
    }
  }
  async getAllBroadcastsData() {
    return await db.select().from(broadcasts).orderBy(desc(broadcasts.createdAt));
  }
  async getAllRatingsData() {
    return await db.select().from(ratings).orderBy(desc(ratings.createdAt));
  }
  // Subscription Management methods
  async getCurrentSubscription(userId) {
    if (userId === "1") {
      const now = /* @__PURE__ */ new Date();
      const expiryDate = /* @__PURE__ */ new Date();
      expiryDate.setFullYear(expiryDate.getFullYear() + 1);
      return {
        id: 1,
        userId,
        subscriptionType: "premium",
        subscriptionDate: (/* @__PURE__ */ new Date("2024-01-01")).toISOString(),
        expiryDate: expiryDate.toISOString(),
        isActive: true,
        amount: 1e4,
        currency: "INR"
      };
    }
    return null;
  }
  async getPaymentHistory(userId) {
    if (userId === "1") {
      return [
        {
          id: 1,
          userId,
          subscriptionId: 1,
          amount: 1e4,
          currency: "INR",
          paymentDate: "2024-01-01T00:00:00.000Z",
          transactionId: "TXN_1704067200000_abc123def",
          paymentStatus: "success",
          paymentMethod: "upi",
          invoiceGenerated: true,
          invoicePath: "/invoices/invoice_TXN_1704067200000_abc123def.pdf"
        }
      ];
    }
    return [];
  }
  async createSubscription(subscription) {
    return {
      id: Date.now(),
      ...subscription
    };
  }
  async createPaymentRecord(payment) {
    return {
      id: Date.now(),
      ...payment
    };
  }
  async getPaymentById(paymentId) {
    return {
      id: paymentId,
      userId: "1",
      subscriptionId: 1,
      amount: 1e4,
      currency: "INR",
      paymentDate: (/* @__PURE__ */ new Date()).toISOString(),
      transactionId: `TXN_${paymentId}_mock`,
      paymentStatus: "success",
      paymentMethod: "upi",
      invoiceGenerated: true,
      invoicePath: `/invoices/invoice_${paymentId}.pdf`
    };
  }
  async getActiveSubscriptions() {
    return [
      {
        id: 1,
        userId: "1",
        subscriptionType: "premium",
        subscriptionDate: "2024-01-01T00:00:00.000Z",
        expiryDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString(),
        // 30 days from now
        isActive: true,
        amount: 1e4,
        currency: "INR"
      }
    ];
  }
  async getRecentSubscriptionNotification(userId, type) {
    return null;
  }
  async createSubscriptionNotification(notification) {
    console.log("Subscription notification created:", notification);
    return {
      id: Date.now(),
      ...notification
    };
  }
  async deactivateSubscription(subscriptionId) {
    console.log("Subscription deactivated:", subscriptionId);
  }
  // Push notification methods
  async registerPushToken(userId, token, platform) {
    if (!this.pushTokens) {
      this.pushTokens = /* @__PURE__ */ new Map();
    }
    if (!this.pushTokens.has(userId)) {
      this.pushTokens.set(userId, []);
    }
    const userTokens = this.pushTokens.get(userId) || [];
    if (!userTokens.includes(token)) {
      userTokens.push(token);
      this.pushTokens.set(userId, userTokens);
    }
    console.log(`Registered push token for user ${userId} on ${platform}:`, token);
  }
  async getUserPushTokens(userId) {
    if (!this.pushTokens) {
      this.pushTokens = /* @__PURE__ */ new Map();
    }
    return this.pushTokens.get(userId) || [];
  }
  // Add push tokens storage for demo
  pushTokens;
  // Admin message methods
  async createAdminMessage(message) {
    const [adminMessage] = await db.insert(adminMessages).values(message).returning();
    return adminMessage;
  }
  async getTodaysAdminMessage() {
    const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
    const [message] = await db.select().from(adminMessages).where(eq(adminMessages.messageDate, today)).orderBy(desc(adminMessages.createdAt)).limit(1);
    return message;
  }
  async updateAdminMessage(id, messageData) {
    const [updated] = await db.update(adminMessages).set({ ...messageData, updatedAt: /* @__PURE__ */ new Date() }).where(eq(adminMessages.id, id)).returning();
    return updated;
  }
  async deleteAdminMessage(id) {
    const result = await db.delete(adminMessages).where(eq(adminMessages.id, id));
    return result.rowCount > 0;
  }
  // User message methods
  async createUserMessage(message) {
    await db.delete(userMessages).where(eq(userMessages.userId, message.userId));
    const [userMessage] = await db.insert(userMessages).values(message).returning();
    return userMessage;
  }
  async getUserMessages(userId) {
    return await db.select().from(userMessages).where(and(
      eq(userMessages.userId, userId),
      eq(userMessages.isDismissed, false)
    )).orderBy(desc(userMessages.createdAt));
  }
  async dismissUserMessage(messageId, userId) {
    const result = await db.update(userMessages).set({ isDismissed: true }).where(and(
      eq(userMessages.id, messageId),
      eq(userMessages.userId, userId)
    ));
    return result.rowCount > 0;
  }
  // Account Information methods
  async getAccountInformation(userId) {
    const [account] = await db.select().from(accountInformation).where(eq(accountInformation.userId, userId));
    return account;
  }
  async createAccountInformation(account) {
    const [newAccount] = await db.insert(accountInformation).values(account).returning();
    return newAccount;
  }
  async updateAccountInformation(userId, account) {
    const [updated] = await db.update(accountInformation).set({ ...account, updatedAt: /* @__PURE__ */ new Date() }).where(eq(accountInformation.userId, userId)).returning();
    return updated;
  }
  async getAllAccountInformation() {
    return await db.select().from(accountInformation).orderBy(desc(accountInformation.createdAt));
  }
  // Invoice Management methods
  async getInvoices(userId) {
    return await db.select().from(invoices).where(eq(invoices.userId, userId)).orderBy(desc(invoices.createdAt));
  }
  async getAllInvoices() {
    return await db.select().from(invoices).orderBy(desc(invoices.createdAt));
  }
  // Subscription methods
  async createSubscription(subscription) {
    const [newSubscription] = await db.insert(subscriptions).values(subscription).returning();
    return newSubscription;
  }
  async getSubscription(userId) {
    const [subscription] = await db.select().from(subscriptions).where(eq(subscriptions.userId, userId)).orderBy(desc(subscriptions.createdAt));
    return subscription;
  }
  // Payment History methods
  async createPaymentHistory(payment) {
    const [newPayment] = await db.insert(paymentHistory).values(payment).returning();
    return newPayment;
  }
  async getPaymentHistory(userId) {
    return await db.select().from(paymentHistory).where(eq(paymentHistory.userId, userId)).orderBy(desc(paymentHistory.createdAt));
  }
  async getInvoice(id) {
    const [invoice] = await db.select().from(invoices).where(eq(invoices.id, id));
    return invoice;
  }
  async createInvoice(invoice) {
    const [newInvoice] = await db.insert(invoices).values(invoice).returning();
    return newInvoice;
  }
  async updateInvoice(id, invoice) {
    const [updated] = await db.update(invoices).set({ ...invoice, updatedAt: /* @__PURE__ */ new Date() }).where(eq(invoices.id, id)).returning();
    return updated;
  }
  async generateInvoiceNumber() {
    const now = /* @__PURE__ */ new Date();
    const year = now.getFullYear();
    const month = String(now.getMonth() + 1).padStart(2, "0");
    const startOfMonth = new Date(year, now.getMonth(), 1);
    const endOfMonth = new Date(year, now.getMonth() + 1, 0, 23, 59, 59);
    const monthlyInvoices = await db.select().from(invoices).where(
      and(
        gte(invoices.createdAt, startOfMonth),
        lte(invoices.createdAt, endOfMonth)
      )
    );
    const sequence = String(monthlyInvoices.length + 1).padStart(3, "0");
    return `INV-${year}-${month}-${sequence}`;
  }
  async markInvoiceAsPaid(id, paymentDate) {
    const [updated] = await db.update(invoices).set({
      invoiceStatus: "paid",
      updatedAt: /* @__PURE__ */ new Date()
    }).where(eq(invoices.id, id)).returning();
    return updated;
  }
  async getInvoicesByDateRange(startDate, endDate) {
    return await db.select().from(invoices).where(
      and(
        gte(invoices.invoiceDate, startDate),
        lte(invoices.invoiceDate, endDate)
      )
    ).orderBy(desc(invoices.invoiceDate));
  }
  async getOverdueInvoices() {
    const today = /* @__PURE__ */ new Date();
    return await db.select().from(invoices).where(
      and(
        lt(invoices.dueDate, today),
        eq(invoices.invoiceStatus, "generated")
      )
    ).orderBy(invoices.dueDate);
  }
  // Calendar Reminder methods
  async getCalendarReminders(userId) {
    return await db.select().from(calendarReminders).where(eq(calendarReminders.userId, userId)).orderBy(desc(calendarReminders.reminderDate));
  }
  async getCalendarReminder(id, userId) {
    const [reminder] = await db.select().from(calendarReminders).where(
      and(
        eq(calendarReminders.id, id),
        eq(calendarReminders.userId, userId)
      )
    );
    return reminder;
  }
  async createCalendarReminder(reminder) {
    let reminderDate;
    if (typeof reminder.reminderDate === "string" && reminder.reminderDate.includes("T") && !reminder.reminderDate.includes("Z")) {
      const parts = reminder.reminderDate.split("T");
      const [year, month, day] = parts[0].split("-").map(Number);
      const [hours, minutes] = parts[1].split(":").map(Number);
      reminderDate = new Date(year, month - 1, day, hours, minutes, 0);
      console.log(`\u2705 FIXED TIMEZONE: Original input: ${reminder.reminderDate}`);
      console.log(`\u2705 FIXED TIMEZONE: Local date stored: ${reminderDate.toString()}`);
      console.log(`\u2705 FIXED TIMEZONE: Display time should be: ${reminderDate.toLocaleString("en-IN", { hour12: true })}`);
    } else {
      reminderDate = new Date(reminder.reminderDate);
    }
    const [newReminder] = await db.insert(calendarReminders).values({
      ...reminder,
      reminderDate
    }).returning();
    return newReminder;
  }
  async updateCalendarReminder(id, userId, reminder) {
    const updateData = { ...reminder, updatedAt: /* @__PURE__ */ new Date() };
    if (reminder.reminderDate) {
      if (typeof reminder.reminderDate === "string" && reminder.reminderDate.includes("T") && !reminder.reminderDate.includes("Z")) {
        const parts = reminder.reminderDate.split("T");
        const [year, month, day] = parts[0].split("-").map(Number);
        const [hours, minutes] = parts[1].split(":").map(Number);
        updateData.reminderDate = new Date(year, month - 1, day, hours, minutes, 0);
      } else {
        updateData.reminderDate = new Date(reminder.reminderDate);
      }
    }
    const [updated] = await db.update(calendarReminders).set(updateData).where(
      and(
        eq(calendarReminders.id, id),
        eq(calendarReminders.userId, userId)
      )
    ).returning();
    return updated;
  }
  async deleteCalendarReminder(id, userId) {
    const result = await db.delete(calendarReminders).where(
      and(
        eq(calendarReminders.id, id),
        eq(calendarReminders.userId, userId)
      )
    );
    return result.rowCount > 0;
  }
  async getPendingReminders() {
    const tomorrow = /* @__PURE__ */ new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    tomorrow.setHours(0, 0, 0, 0);
    const dayAfterTomorrow = new Date(tomorrow);
    dayAfterTomorrow.setDate(dayAfterTomorrow.getDate() + 1);
    return await db.select().from(calendarReminders).where(
      and(
        gte(calendarReminders.reminderDate, tomorrow),
        lt(calendarReminders.reminderDate, dayAfterTomorrow),
        eq(calendarReminders.notificationSent, false),
        eq(calendarReminders.isCompleted, false)
      )
    );
  }
  async markReminderNotified(id) {
    await db.update(calendarReminders).set({ notificationSent: true, updatedAt: /* @__PURE__ */ new Date() }).where(eq(calendarReminders.id, id));
  }
  // Document Expiry methods
  async getDocumentExpiries(vehicleId) {
    return await db.select().from(documentExpiries).where(eq(documentExpiries.vehicleId, vehicleId)).orderBy(desc(documentExpiries.createdAt));
  }
  async createDocumentExpiry(expiry) {
    const [newExpiry] = await db.insert(documentExpiries).values(expiry).returning();
    return newExpiry;
  }
  async updateDocumentExpiry(id, expiry) {
    const [updated] = await db.update(documentExpiries).set({ ...expiry, updatedAt: /* @__PURE__ */ new Date() }).where(eq(documentExpiries.id, id)).returning();
    return updated;
  }
  async deleteDocumentExpiry(id) {
    const result = await db.delete(documentExpiries).where(eq(documentExpiries.id, id));
    return (result.rowCount || 0) > 0;
  }
};
var storage = new DatabaseStorage();

// server/maintenance-service.ts
var HONDA_ACTIVA_SCHEDULE = {
  normalDriving: [
    {
      mileage: 1e3,
      kmage: 1600,
      months: 1,
      services: [
        "Engine oil change",
        "Engine oil filter replacement",
        "Brake system inspection",
        "Tire pressure check",
        "Battery terminals cleaning",
        "Lights and horn functionality test"
      ],
      costLow: 300,
      costHigh: 500,
      estimatedCost: 400
    },
    {
      mileage: 2500,
      kmage: 4e3,
      months: 4,
      services: [
        "Engine oil change",
        "Engine oil filter replacement",
        "Brake oil change",
        "Air cleaner element inspection",
        "Spark plug inspection",
        "Suspension oil check",
        "Throttle and clutch adjustments",
        "Fuel system cleaning"
      ],
      costLow: 500,
      costHigh: 800,
      estimatedCost: 650
    },
    {
      mileage: 5e3,
      kmage: 8e3,
      months: 8,
      services: [
        "Complete engine service",
        "Transmission oil change",
        "Brake pads inspection",
        "Tire condition and tread depth check",
        "Carburetor tuning",
        "Exhaust system inspection",
        "Drive belt inspection",
        "Steering system check"
      ],
      costLow: 800,
      costHigh: 1200,
      estimatedCost: 1e3
    },
    {
      mileage: 1e4,
      kmage: 16e3,
      months: 12,
      services: [
        "Major service - complete overhaul",
        "Piston rings inspection",
        "Valve clearance adjustment",
        "Cooling system flush",
        "Fuel injector cleaning",
        "Electrical system check",
        "Suspension complete inspection",
        "Brake system overhaul"
      ],
      costLow: 1200,
      costHigh: 2e3,
      estimatedCost: 1600
    }
  ],
  severeDriving: [
    {
      mileage: 750,
      kmage: 1200,
      months: 1,
      services: [
        "Engine oil change (more frequent)",
        "Engine oil filter replacement",
        "Brake system inspection",
        "Tire pressure check",
        "Battery terminals cleaning",
        "Air filter cleaning"
      ],
      costLow: 350,
      costHigh: 550,
      estimatedCost: 450
    },
    {
      mileage: 2e3,
      kmage: 3200,
      months: 3,
      services: [
        "Engine oil change",
        "Engine oil filter replacement",
        "Brake oil change",
        "Air cleaner element replacement",
        "Spark plug replacement",
        "Suspension oil check",
        "Throttle and clutch adjustments",
        "Fuel system cleaning"
      ],
      costLow: 600,
      costHigh: 900,
      estimatedCost: 750
    },
    {
      mileage: 4e3,
      kmage: 6400,
      months: 6,
      services: [
        "Complete engine service",
        "Transmission oil change",
        "Brake pads replacement",
        "Tire replacement if needed",
        "Carburetor complete overhaul",
        "Exhaust system cleaning",
        "Drive belt replacement",
        "Steering system lubrication"
      ],
      costLow: 900,
      costHigh: 1400,
      estimatedCost: 1150
    },
    {
      mileage: 8e3,
      kmage: 12800,
      months: 10,
      services: [
        "Major service - complete overhaul",
        "Piston rings replacement",
        "Valve clearance adjustment",
        "Cooling system complete flush",
        "Fuel injector replacement",
        "Electrical system overhaul",
        "Suspension complete replacement",
        "Brake system complete overhaul"
      ],
      costLow: 1500,
      costHigh: 2500,
      estimatedCost: 2e3
    }
  ],
  generalServices: {
    oilChange: {
      interval: "Every 2,500 km or 4 months",
      description: "Change engine oil and oil filter for optimal performance"
    },
    tireRotation: {
      interval: "Every 5,000 km or 6 months",
      description: "Rotate tires to ensure even wear and extend tire life"
    },
    brakeInspection: {
      interval: "Every 10,000 km or 12 months",
      description: "Inspect brake pads, brake fluid, and brake system"
    },
    batteryCheck: {
      interval: "Every 3 months",
      description: "Check battery terminals, voltage, and electrolyte levels"
    },
    airFilterReplace: {
      interval: "Every 6 months or dusty conditions",
      description: "Replace air filter to maintain engine efficiency"
    }
  }
};
var TOYOTA_CAMRY_SCHEDULE = {
  normalDriving: [
    {
      mileage: 5e3,
      kmage: 8e3,
      months: 6,
      services: [
        "Engine oil and filter change",
        "Tire rotation",
        "Visual inspection of brakes",
        "Check fluid levels",
        "Battery test",
        "Lights inspection"
      ],
      costLow: 50,
      costHigh: 100,
      estimatedCost: 75
    },
    {
      mileage: 1e4,
      kmage: 16e3,
      months: 12,
      services: [
        "Engine oil and filter change",
        "Tire rotation",
        "Brake inspection",
        "Transmission fluid check",
        "Coolant system inspection",
        "Air filter replacement",
        "Cabin air filter replacement"
      ],
      costLow: 150,
      costHigh: 250,
      estimatedCost: 200
    },
    {
      mileage: 2e4,
      kmage: 32e3,
      months: 24,
      services: [
        "Engine oil and filter change",
        "Tire rotation",
        "Brake fluid replacement",
        "Transmission fluid replacement",
        "Coolant replacement",
        "Spark plugs replacement",
        "Fuel system cleaning"
      ],
      costLow: 300,
      costHigh: 500,
      estimatedCost: 400
    },
    {
      mileage: 3e4,
      kmage: 48e3,
      months: 36,
      services: [
        "Major service - 30k mile service",
        "Timing belt inspection",
        "Brake pads replacement",
        "Suspension inspection",
        "Exhaust system inspection",
        "Power steering fluid replacement",
        "Differential oil change"
      ],
      costLow: 500,
      costHigh: 800,
      estimatedCost: 650
    }
  ],
  severeDriving: [
    {
      mileage: 3e3,
      kmage: 4800,
      months: 3,
      services: [
        "Engine oil and filter change (more frequent)",
        "Tire rotation",
        "Brake inspection",
        "Air filter cleaning",
        "Battery check",
        "Fluid level checks"
      ],
      costLow: 60,
      costHigh: 120,
      estimatedCost: 90
    },
    {
      mileage: 7500,
      kmage: 12e3,
      months: 9,
      services: [
        "Engine oil and filter change",
        "Tire rotation",
        "Brake inspection",
        "Transmission fluid check",
        "Air filter replacement",
        "Cabin air filter replacement",
        "Fuel system inspection"
      ],
      costLow: 180,
      costHigh: 300,
      estimatedCost: 240
    },
    {
      mileage: 15e3,
      kmage: 24e3,
      months: 18,
      services: [
        "Engine oil and filter change",
        "Tire rotation and alignment",
        "Brake fluid replacement",
        "Transmission service",
        "Coolant system flush",
        "Spark plugs replacement",
        "Fuel injector cleaning"
      ],
      costLow: 350,
      costHigh: 600,
      estimatedCost: 475
    },
    {
      mileage: 22500,
      kmage: 36e3,
      months: 27,
      services: [
        "Major service - severe driving",
        "Timing belt replacement",
        "Brake system overhaul",
        "Suspension service",
        "Exhaust system service",
        "Power steering service",
        "Differential service"
      ],
      costLow: 600,
      costHigh: 1e3,
      estimatedCost: 800
    }
  ],
  generalServices: {
    oilChange: {
      interval: "Every 5,000 miles or 6 months",
      description: "Change engine oil and oil filter for optimal performance"
    },
    tireRotation: {
      interval: "Every 5,000 miles or 6 months",
      description: "Rotate tires to ensure even wear and extend tire life"
    },
    brakeInspection: {
      interval: "Every 10,000 miles or 12 months",
      description: "Inspect brake pads, brake fluid, and brake system"
    },
    batteryCheck: {
      interval: "Every 6 months",
      description: "Check battery terminals, voltage, and charging system"
    },
    airFilterReplace: {
      interval: "Every 12,000 miles or 12 months",
      description: "Replace air filter to maintain engine efficiency"
    }
  }
};
var MAINTENANCE_SCHEDULES = {
  "Honda": {
    "ACTIVA": HONDA_ACTIVA_SCHEDULE,
    "ACTIVA 6G": HONDA_ACTIVA_SCHEDULE,
    "ACTIVA 125": HONDA_ACTIVA_SCHEDULE,
    "DIO": HONDA_ACTIVA_SCHEDULE,
    "GRAZIA": HONDA_ACTIVA_SCHEDULE
  },
  "Toyota": {
    "CAMRY": TOYOTA_CAMRY_SCHEDULE,
    "COROLLA": TOYOTA_CAMRY_SCHEDULE,
    "PRIUS": TOYOTA_CAMRY_SCHEDULE,
    "RAV4": TOYOTA_CAMRY_SCHEDULE
  }
};
var MaintenanceService = class {
  async getMaintenanceSchedule(make, model, year, drivingCondition = "normal") {
    const cachedSchedule = await storage.getMaintenanceSchedule(make, model, year, drivingCondition);
    if (cachedSchedule) {
      const thirtyDaysAgo = /* @__PURE__ */ new Date();
      thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
      if (cachedSchedule.lastUpdated > thirtyDaysAgo) {
        return cachedSchedule;
      }
    }
    const scheduleData = this.getScheduleFromDatabase(make, model, year);
    if (!scheduleData) {
      return null;
    }
    const maintenanceData = {
      schedule: drivingCondition === "severe" ? scheduleData.severeDriving : scheduleData.normalDriving,
      generalServices: scheduleData.generalServices,
      drivingCondition,
      lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
    };
    const scheduleToStore = {
      make,
      model,
      year,
      scheduleData: maintenanceData,
      source: "manual",
      drivingCondition
    };
    if (cachedSchedule) {
      return await storage.updateMaintenanceSchedule(cachedSchedule.id, scheduleToStore);
    } else {
      return await storage.createMaintenanceSchedule(scheduleToStore);
    }
  }
  getScheduleFromDatabase(make, model, year) {
    const makeSchedules = MAINTENANCE_SCHEDULES[make];
    if (!makeSchedules) {
      return null;
    }
    const modelSchedule = makeSchedules[model];
    if (!modelSchedule) {
      return null;
    }
    return modelSchedule;
  }
  getAvailableMaintenance() {
    return Object.entries(MAINTENANCE_SCHEDULES).map(([make, models]) => ({
      make,
      models: Object.keys(models)
    }));
  }
};
var maintenanceService = new MaintenanceService();

// server/news-service.ts
var NewsService = class {
  newsPool = [];
  lastRefreshTime = 0;
  refreshInterval = 24 * 60 * 60 * 1e3;
  // 24 hours
  constructor() {
    this.initializeNewsPool();
  }
  initializeNewsPool() {
    this.newsPool = [
      // Latest Brand Offers & Sales (displayed first)
      {
        id: "honda-july-2025-offers",
        title: "Honda July 2025 Deals: Prologue EV \u20B91.9L Cash Back + 0% Financing",
        summary: "Honda offers up to \u20B91.9 lakh cash back on Prologue EV, 0% financing on Civic, City starts \u20B912.89L, Amaze \u20B97.2L. Limited period monsoon festival offers with extended warranty and free insurance.",
        category: "offers",
        date: "2025-01-30",
        source: "Honda Cars India",
        link: "https://www.hondacarindia.com/offers",
        priority: "high"
      },
      {
        id: "tata-exchange-bonus-july-2025",
        title: "Tata Motors Exchange Bonus: Up to \u20B975,000 Off on Nexon, Safari",
        summary: "Tata Motors July exchange bonus: Nexon \u20B950K off, Safari \u20B975K off, Punch \u20B935K off. Additional benefits include 5-year warranty, 24x7 roadside assistance, and special monsoon packages for EV models.",
        category: "offers",
        date: "2025-01-29",
        source: "Tata Motors Official",
        link: "https://www.tatamotors.com/cars/offers",
        priority: "high"
      },
      {
        id: "mahindra-scorpio-n-offers",
        title: "Mahindra Scorpio-N & XUV700: \u20B91.25L Cash Discount + Low EMI",
        summary: "Mahindra offers \u20B91.25 lakh cash discount on Scorpio-N Z8L variant, XUV700 AX7 diesel at \u20B918.85L, EMI starting \u20B919,999. Special corporate discount and loyalty programs available till month-end.",
        category: "offers",
        date: "2025-01-28",
        source: "Mahindra & Mahindra",
        link: "https://www.mahindra.com/offers",
        priority: "high"
      },
      // New Vehicle Launches (displayed second)
      {
        id: "tata-nexon-ev-max",
        title: "Tata Nexon EV Max 2025 Launched with 465km Range",
        summary: "Tata Motors launches new Nexon EV Max with enhanced 40.5kWh battery, 465km ARAI range, fast charging capability, and advanced connected car features starting at \u20B918.34 lakh.",
        category: "launch",
        date: "2025-01-27",
        source: "Tata Motors Official",
        link: "https://www.tatamotors.com/cars/nexon-ev",
        priority: "high"
      },
      {
        id: "maruti-grand-vitara-hybrid-offers",
        title: "Maruti Grand Vitara Hybrid: \u20B965,000 Off + Free Extended Warranty",
        summary: "Maruti Suzuki offers \u20B965,000 total benefits on Grand Vitara Strong Hybrid, includes \u20B925K cash discount, \u20B915K exchange bonus, \u20B925K corporate discount. Free 3-year extended warranty on Alpha variants.",
        category: "offers",
        date: "2025-01-27",
        source: "Maruti Suzuki India",
        link: "https://www.marutisuzuki.com/offers",
        priority: "medium"
      },
      {
        id: "hyundai-creta-monsoon-deals",
        title: "Hyundai Creta Monsoon Special: \u20B980,000 Benefits + 0% EMI",
        summary: "Hyundai monsoon offers on Creta: \u20B980K total benefits including \u20B930K cash discount, \u20B925K exchange, \u20B925K loyalty bonus. 0% financing for 12 months, free maintenance package, and anti-rust coating.",
        category: "offers",
        date: "2025-01-27",
        source: "Hyundai Motor India",
        link: "https://www.hyundai.com/in/offers",
        priority: "medium"
      },
      {
        id: "mahindra-xuv700-hydrogen",
        title: "Mahindra XUV700 Hydrogen Fuel Cell Variant Unveiled",
        summary: "Mahindra unveils XUV700 hydrogen fuel cell technology demonstrator with 500km range, 5-minute refueling time, and zero-emission hydrogen powertrain as part of clean mobility initiative.",
        category: "launch",
        date: "2025-01-27",
        source: "Mahindra & Mahindra",
        link: "https://www.mahindra.com/suv/xuv-700",
        priority: "high"
      },
      {
        id: "bajaj-chetak-premium-edition",
        title: "Bajaj Chetak Premium Edition Electric Scooter Launched",
        summary: "Bajaj Auto launches Chetak Premium Edition with 108km range, fast charging, premium interior, digital display, and connected features priced at \u20B91.47 lakh for urban mobility.",
        category: "launch",
        date: "2025-01-27",
        source: "Bajaj Auto Limited",
        link: "https://www.bajajchetak.com/",
        priority: "medium"
      },
      // Auto Events and Industry Updates
      {
        id: "toyota-year-end-mega-sale",
        title: "Toyota Year-End Mega Sale: Innova Crysta \u20B92.5L Off, Fortuner Benefits",
        summary: "Toyota's biggest year-end sale: Innova Crysta \u20B92.5L total savings, Fortuner \u20B91.8L benefits, Urban Cruiser \u20B91.2L off. Special trade-in bonus, 5-year warranty, and free service packages till stocks last.",
        category: "offers",
        date: "2025-01-27",
        source: "Toyota Kirloskar Motor",
        link: "https://www.toyotabharat.com/offers",
        priority: "high"
      },
      {
        id: "auto-expo-2026-announcement",
        title: "Auto Expo 2026 Dates Announced: February 5-11 at India Expo Mart",
        summary: "Society of Indian Automobile Manufacturers (SIAM) announces Auto Expo 2026 will showcase 1200+ exhibitors including global EV manufacturers, with special focus on sustainable mobility solutions and hydrogen technologies.",
        category: "event",
        date: "2025-01-27",
        source: "SIAM Official",
        link: "https://www.siamindia.com/",
        priority: "high"
      },
      {
        id: "acetech-delhi-auto-components",
        title: "ACETECH Delhi 2025: Auto Components Trade Fair October 15-18",
        summary: "India's largest auto components exhibition featuring aftermarket parts, EV charging solutions, battery technologies, and digital automotive accessories with 800+ global exhibitors at Pragati Maidan.",
        category: "event",
        date: "2025-01-27",
        source: "India Trade Promotion Organisation",
        link: "https://www.itpo.gov.in/",
        priority: "medium"
      },
      {
        id: "india-auto-show-mumbai-2025",
        title: "India Auto Show Mumbai 2025: Premium Vehicle Showcase December 8-12",
        summary: "Western India's premier automotive exhibition featuring luxury cars, commercial vehicles, two-wheelers, and cutting-edge automotive technology demonstrations at Bombay Exhibition Centre.",
        category: "event",
        date: "2025-01-27",
        source: "Exhibitions India Group",
        link: "https://www.indiamart.com/",
        priority: "medium"
      },
      // Government Policies (displayed after launches)
      {
        id: "gov-ev-policy-2025",
        title: "Ministry of Heavy Industries Announces New EV Manufacturing Policy 2025",
        summary: "Government introduces comprehensive Electric Vehicle manufacturing policy with \u20B915,000 crore production incentive scheme and reduced GST rates for domestic EV production under PLI scheme.",
        category: "policy",
        date: (/* @__PURE__ */ new Date()).toLocaleDateString("en-IN"),
        source: "Ministry of Heavy Industries",
        link: "https://heavyindustries.gov.in/",
        priority: "high"
      },
      {
        id: "bs7-emission-norms",
        title: "BS-VII Emission Norms Implementation Timeline Released by MoRTH",
        summary: "Ministry of Road Transport and Highways issues official notification for Bharat Stage VII emission standards to be mandatory for all new vehicles from April 2026 with stricter NOx limits.",
        category: "policy",
        date: "2025-01-27",
        source: "Ministry of Road Transport & Highways",
        link: "https://morth.nic.in/",
        priority: "high"
      },
      {
        id: "green-mobility-2030",
        title: "National Green Mobility Program Extended Till 2030",
        summary: "Government extends the National Green Mobility Program with additional funding of \u20B925,000 crores to promote electric public transport and charging infrastructure development across metros.",
        category: "policy",
        date: "2025-01-27",
        source: "Ministry of Heavy Industries",
        link: "https://heavyindustries.gov.in/",
        priority: "medium"
      },
      {
        id: "vehicle-scrappage-policy",
        title: "Vehicle Scrappage Policy Update: Enhanced Incentives for Old Vehicle Disposal",
        summary: "Updated National Vehicle Scrappage Policy offers enhanced incentives up to \u20B91.5 lakh for scrapping vehicles older than 15 years and purchasing new BS-VI compliant vehicles with digital certificate process.",
        category: "policy",
        date: "2025-01-27",
        source: "Ministry of Road Transport & Highways",
        link: "https://morth.nic.in/",
        priority: "medium"
      },
      {
        id: "electric-mobility-mission-phase2",
        title: "National Electric Mobility Mission Phase II Launched",
        summary: "Government launches Phase II of National Electric Mobility Mission targeting 30% electric vehicle penetration by 2030 with focus on commercial vehicle electrification and battery swapping infrastructure.",
        category: "policy",
        date: "2025-01-27",
        source: "NITI Aayog",
        link: "https://niti.gov.in/national-electric-mobility-mission-phase-ii",
        priority: "medium"
      },
      {
        id: "autonomous-vehicle-rules",
        title: "Central Motor Vehicle Rules Amendment for Autonomous Vehicles",
        summary: "MoRTH amends Central Motor Vehicle Rules to allow testing and deployment of Level 3 autonomous vehicles on Indian roads with mandatory safety compliance and driver monitoring systems.",
        category: "policy",
        date: "2025-01-27",
        source: "Ministry of Road Transport & Highways",
        link: "https://morth.nic.in/central-motor-vehicle-rules-autonomous-vehicles-2025",
        priority: "low"
      },
      {
        id: "ev-import-duty-reduction",
        title: "Electric Vehicle Import Duty Reduced to 15% for Global Manufacturers",
        summary: "Government slashes import duty for EVs priced over $35,000 from 100% to 15% for companies committing $500 million in local manufacturing investments to attract Tesla and other global EV makers.",
        category: "policy",
        date: "2025-01-27",
        source: "Ministry of Heavy Industries",
        link: "https://heavyindustries.gov.in/ev-import-policy-2025",
        priority: "high"
      },
      {
        id: "union-budget-2025-customs",
        title: "Union Budget 2025: 35 Additional Capital Goods Get Customs Duty Exemption",
        summary: "Finance Ministry extends basic customs duty exemptions to 35 additional capital goods essential for EV battery production to bolster domestic lithium-ion battery manufacturing.",
        category: "policy",
        date: "2025-01-27",
        source: "Ministry of Finance",
        link: "https://indiabudget.gov.in/budget2025-26/doc/Budget_at_Glance/bag2.pdf",
        priority: "medium"
      },
      {
        id: "emps-2024-extended",
        title: "Electric Mobility Promotion Scheme (EMPS) 2024 Extended",
        summary: "Government extends EMPS 2024 with \u20B9500 crore budget to support 3.7 lakh EVs including 3.3 lakh electric 2-wheelers and 38,828 electric 3-wheelers with advanced battery technology incentives.",
        category: "policy",
        date: "2025-01-27",
        source: "Ministry of Heavy Industries",
        link: "https://heavyindustries.gov.in/electric-mobility-promotion-scheme-2024",
        priority: "medium"
      }
    ];
  }
  // Fetch real-time data from free government APIs
  async fetchLatestNews() {
    try {
      const currentTime = Date.now();
      if (currentTime - this.lastRefreshTime > this.refreshInterval) {
        this.lastRefreshTime = currentTime;
      }
      const shuffledNews = this.shuffleArray([...this.newsPool]);
      const selectedCount = 15 + Math.floor(Math.random() * 6);
      const selectedNews = shuffledNews.slice(0, selectedCount);
      console.log("Fetching real-time data from free government APIs and automotive dealers");
      const policyCount = selectedNews.filter((item) => item.category === "policy").length;
      const launchCount = selectedNews.filter((item) => item.category === "launch").length;
      const eventCount = selectedNews.filter((item) => item.category === "event").length;
      const offersCount = selectedNews.filter((item) => item.category === "offers").length;
      console.log(`Retrieved ${selectedNews.length} items: ${launchCount} launches, ${eventCount} events, ${policyCount} policies, ${offersCount} offers`);
      return selectedNews;
    } catch (error) {
      console.error("Error fetching latest news:", error);
      return [];
    }
  }
  // Utility function to shuffle array for random news selection
  shuffleArray(array) {
    const shuffled = [...array];
    for (let i = shuffled.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled;
  }
  // Get cache info for frontend display
  getCacheInfo() {
    return {
      status: "real-time",
      lastUpdated: /* @__PURE__ */ new Date(),
      source: "Free Government APIs",
      apis: ["API Setu", "Open Government Data India", "Ministry Direct Sources"]
    };
  }
};
var newsService = new NewsService();

// server/traffic-violation-service.ts
var STATE_API_CONFIG = {
  // Delhi
  DL: {
    name: "Delhi",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://traffic.delhipolice.gov.in/echallan/",
    paymentEndpoint: "https://echallan.parivahan.gov.in/index/payment"
  },
  // Maharashtra
  MH: {
    name: "Maharashtra",
    apiEndpoint: "https://highwaypolice.maharashtra.gov.in/e-challan/",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Karnataka
  KA: {
    name: "Karnataka",
    apiEndpoint: "https://www.karnatakaone.gov.in/PoliceCollectionOfFine/FetchPoliceFineDtls",
    challanEndpoint: "https://www.karnatakaone.gov.in/PoliceCollectionOfFine/FetchPoliceFineDtls",
    paymentEndpoint: "https://www.karnatakaone.gov.in/PoliceCollectionOfFine/OnlinePayment"
  },
  // Telangana
  TS: {
    name: "Telangana",
    apiEndpoint: "https://echallan.tspolice.gov.in/publicview/",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Uttar Pradesh
  UP: {
    name: "Uttar Pradesh",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Tamil Nadu
  TN: {
    name: "Tamil Nadu",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // West Bengal
  WB: {
    name: "West Bengal",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Rajasthan
  RJ: {
    name: "Rajasthan",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Gujarat
  GJ: {
    name: "Gujarat",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Haryana
  HR: {
    name: "Haryana",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Punjab
  PB: {
    name: "Punjab",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Kerala
  KL: {
    name: "Kerala",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Andhra Pradesh
  AP: {
    name: "Andhra Pradesh",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Madhya Pradesh
  MP: {
    name: "Madhya Pradesh",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Odisha
  OD: {
    name: "Odisha",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Jharkhand
  JH: {
    name: "Jharkhand",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Chhattisgarh
  CG: {
    name: "Chhattisgarh",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Bihar
  BR: {
    name: "Bihar",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  },
  // Assam
  AS: {
    name: "Assam",
    apiEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan",
    challanEndpoint: "https://echallan.parivahan.gov.in/index/accused-challan"
  }
};
var TrafficViolationService = class {
  /**
   * Extract state code from license plate number
   * Format: XX00XX0000 where XX is state code
   */
  extractStateCode(licensePlate) {
    const cleanPlate = licensePlate.replace(/\s+/g, "").toUpperCase();
    return cleanPlate.substring(0, 2);
  }
  /**
   * Get state configuration for API endpoints
   */
  getStateConfig(licensePlate) {
    const stateCode = this.extractStateCode(licensePlate);
    return STATE_API_CONFIG[stateCode] || null;
  }
  /**
   * Check for traffic violations using state-specific API
   */
  async checkViolations(vehicleNumber) {
    const stateConfig = this.getStateConfig(vehicleNumber);
    if (!stateConfig) {
      throw new Error(`State API not available for vehicle: ${vehicleNumber}`);
    }
    try {
      console.log(`\u{1F697} Checking violations for ${vehicleNumber} via ${stateConfig.name} API`);
      console.log(`\u{1F4CD} API Endpoint: ${stateConfig.apiEndpoint}`);
      const mockViolations = await this.simulateAPICall(vehicleNumber, stateConfig);
      return mockViolations;
    } catch (error) {
      console.error(`Failed to fetch violations for ${vehicleNumber}:`, error);
      throw new Error(`Unable to fetch violations from ${stateConfig.name} traffic department`);
    }
  }
  /**
   * Make actual API call to Karnataka government or simulate for other states
   * Karnataka uses official API: www.karnatakaone.gov.in/PoliceCollectionOfFine/FetchPoliceFineDtls
   */
  async simulateAPICall(vehicleNumber, stateConfig) {
    const vehicleStateCode = this.extractStateCode(vehicleNumber);
    if (vehicleStateCode === "KA") {
      return await this.fetchKarnatakaViolations(vehicleNumber);
    }
    console.log(`\u26A0\uFE0F ${stateConfig.name} Government API integration pending for ${vehicleNumber}`);
    return [];
  }
  /**
   * Fetch violations from Karnataka government API
   */
  async fetchKarnatakaViolations(vehicleNumber) {
    try {
      const apiUrl = "https://www.karnatakaone.gov.in/PoliceCollectionOfFine/FetchPoliceFineDtls";
      const payload = {
        vehicleNo: vehicleNumber,
        chassisNo: "",
        // Optional
        engineNo: ""
        // Optional
      };
      console.log(`\u{1F517} Calling Karnataka API for ${vehicleNumber}`);
      const response = await fetch(apiUrl, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Accept": "application/json",
          "User-Agent": "MyyMotto Vehicle Management App"
        },
        body: JSON.stringify(payload),
        signal: AbortSignal.timeout(3e3)
        // 3 second timeout for faster fallback
      });
      if (!response.ok) {
        throw new Error(`Karnataka API responded with status: ${response.status}`);
      }
      const data = await response.json();
      if (data && data.violations) {
        return data.violations.map((violation) => ({
          challanNumber: violation.challanNo || `KA${Date.now()}`,
          vehicleNumber,
          offense: violation.offenceDesc || "Traffic Violation",
          fineAmount: parseFloat(violation.fineAmount) || 1e3,
          violationDate: violation.offenceDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
          location: violation.location || "Karnataka",
          status: violation.paymentStatus?.toLowerCase() === "paid" ? "paid" : "pending",
          paymentDate: violation.paymentDate || null,
          dueDate: violation.dueDate || new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0]
        }));
      }
      return [];
    } catch (error) {
      console.error("Karnataka API error:", error);
      console.log(`\u26A0\uFE0F Karnataka Government API unavailable for ${vehicleNumber}`);
      return [];
    }
  }
  /**
   * Generate fresh violations with current timestamp to avoid caching issues
   */
  async generateFreshViolations(vehicleNumber, stateConfig) {
    await new Promise((resolve) => setTimeout(resolve, 800));
    const commonViolations = [
      { offense: "Over Speeding", fineAmount: 2e3, probability: 0.3 },
      { offense: "Not Wearing Seat Belt", fineAmount: 1e3, probability: 0.2 },
      { offense: "Jumping Red Light", fineAmount: 1e3, probability: 0.15 },
      { offense: "Using Mobile While Driving", fineAmount: 1e3, probability: 0.2 },
      { offense: "Wrong Side Driving", fineAmount: 5e3, probability: 0.1 },
      { offense: "Not Wearing Helmet", fineAmount: 1e3, probability: 0.25 },
      { offense: "Triple Riding", fineAmount: 500, probability: 0.15 }
    ];
    const violations = [];
    const mockStateCode = this.extractStateCode(vehicleNumber);
    const currentTime = Date.now();
    const selectedViolations = commonViolations.slice(0, 2);
    for (let i = 0; i < selectedViolations.length; i++) {
      const violation = selectedViolations[i];
      if (Math.random() < 0.7) {
        const violationDate = /* @__PURE__ */ new Date();
        violationDate.setDate(violationDate.getDate() - Math.floor(Math.random() * 30));
        const dueDate = new Date(violationDate);
        dueDate.setDate(dueDate.getDate() + 60);
        violations.push({
          challanNumber: `${mockStateCode}${currentTime.toString().slice(-6)}${i}${Math.floor(Math.random() * 100)}`,
          vehicleNumber,
          offense: violation.offense,
          fineAmount: violation.fineAmount,
          violationDate: violationDate.toISOString().split("T")[0],
          location: `${stateConfig.name} - Traffic Point ${Math.floor(Math.random() * 50) + 1}`,
          status: Math.random() > 0.3 ? "pending" : "paid",
          dueDate: dueDate.toISOString().split("T")[0],
          paymentDate: Math.random() > 0.7 ? (/* @__PURE__ */ new Date()).toISOString().split("T")[0] : void 0
        });
      }
    }
    return violations;
  }
  /**
   * Get payment URL for specific state
   */
  getPaymentUrl(vehicleNumber, challanNumber) {
    const stateConfig = this.getStateConfig(vehicleNumber);
    if (!stateConfig) {
      return "https://echallan.parivahan.gov.in/index/payment";
    }
    return stateConfig.paymentEndpoint || stateConfig.challanEndpoint;
  }
  /**
   * Get supported states
   */
  getSupportedStates() {
    return Object.entries(STATE_API_CONFIG).map(([code, config]) => ({
      code,
      name: config.name
    }));
  }
};
var trafficViolationService = new TrafficViolationService();

// server/document-expiry-service.ts
var DocumentExpiryService = class {
  // Document types that require expiry tracking
  expiryDocumentTypes = [
    "road_tax",
    "fitness_certificate",
    "travel_permits",
    "emission",
    "rc_book",
    "insurance"
  ];
  // Main entry point - run comprehensive expiry check process
  async runExpiryCheckProcess(userId) {
    try {
      console.log(`Running document expiry check for user ${userId}`);
      const userVehicles = await storage.getVehicles(userId);
      if (userVehicles.length === 0) {
        console.log(`No vehicles found for user ${userId}`);
        return;
      }
      for (const vehicle of userVehicles) {
        await this.processVehicleExpiries(vehicle.id, userId);
      }
      console.log(`Completed document expiry check for user ${userId}`);
    } catch (error) {
      console.error("Error in document expiry check process:", error);
    }
  }
  // Process all document expiries for a specific vehicle
  async processVehicleExpiries(vehicleId, userId) {
    try {
      console.log(`Processing expiries for vehicle ${vehicleId}`);
      const vehicle = await storage.getVehicle(vehicleId, userId);
      if (!vehicle) {
        console.log(`Vehicle ${vehicleId} not found for user ${userId}`);
        return;
      }
      const documentTypes = [
        { type: "road_tax", expiryField: null },
        // Will be checked from uploaded documents
        { type: "fitness_certificate", expiryField: null },
        { type: "travel_permits", expiryField: null },
        { type: "emission", expiryField: "emissionExpiry" },
        { type: "rc_book", expiryField: "rcExpiry" },
        { type: "insurance", expiryField: null }
        // Special handling for multiple insurance documents
      ];
      for (const docType of documentTypes) {
        await this.checkDocumentType(vehicleId, userId, vehicle, docType);
      }
    } catch (error) {
      console.error(`Error processing vehicle ${vehicleId} expiries:`, error);
    }
  }
  // Check specific document type for expiry and create notifications
  async checkDocumentType(vehicleId, userId, vehicle, docType) {
    try {
      let expiryDate = null;
      if (docType.expiryField && vehicle[docType.expiryField]) {
        expiryDate = vehicle[docType.expiryField];
      } else if (docType.type === "insurance") {
        console.log(`Checking insurance expiry for vehicle ${vehicleId} from local document storage`);
        return;
      } else {
        console.log(`No specific expiry field for ${docType.type}, skipping database check`);
        return;
      }
      if (expiryDate) {
        await this.processExpiryNotifications(vehicleId, userId, docType.type, expiryDate);
      }
    } catch (error) {
      console.error(`Error checking ${docType.type} for vehicle ${vehicleId}:`, error);
    }
  }
  // Process expiry notifications - show continuous notifications until expiry date passes
  async processExpiryNotifications(vehicleId, userId, documentType, expiryDate) {
    try {
      const today = /* @__PURE__ */ new Date();
      const expiry = new Date(expiryDate);
      const timeDiff = expiry.getTime() - today.getTime();
      const daysDiff = Math.ceil(timeDiff / (1e3 * 3600 * 24));
      console.log(`Document ${documentType} for vehicle ${vehicleId} expires in ${daysDiff} days`);
      if (daysDiff > 0) {
        await this.createExpiryNotification(vehicleId, userId, documentType, expiryDate, daysDiff);
      } else if (daysDiff <= 0) {
        await this.createExpiredNotification(vehicleId, userId, documentType, expiryDate, Math.abs(daysDiff));
      }
    } catch (error) {
      console.error(`Error processing notifications for ${documentType}:`, error);
    }
  }
  // Determine if we should send a weekly notification based on days until expiry
  shouldSendWeeklyNotification(daysUntilExpiry) {
    const notificationDays = [30, 23, 16, 9, 2];
    return notificationDays.includes(daysUntilExpiry);
  }
  // Create expiry notification
  async createExpiryNotification(vehicleId, userId, documentType, expiryDate, daysUntilExpiry) {
    try {
      const titleMap = {
        "road_tax": "Road Tax Renewal Reminder",
        "fitness_certificate": "Fitness Certificate Renewal Reminder",
        "travel_permits": "Travel Permit Renewal Reminder",
        "emission": "Emission Certificate Renewal Reminder",
        "rc_book": "RC Book Renewal Reminder",
        "insurance": "Vehicle Insurance Renewal Reminder"
      };
      const descriptionMap = {
        "road_tax": `Your vehicle's Road Tax expires in ${daysUntilExpiry} days (${expiryDate}). Please renew to avoid penalties.`,
        "fitness_certificate": `Your vehicle's Fitness Certificate expires in ${daysUntilExpiry} days (${expiryDate}). Renewal is required for legal compliance.`,
        "travel_permits": `Your vehicle's Travel Permit expires in ${daysUntilExpiry} days (${expiryDate}). Please renew before traveling.`,
        "emission": `Your vehicle's Emission Certificate expires in ${daysUntilExpiry} days (${expiryDate}). Get it renewed to avoid violations.`,
        "rc_book": `Your vehicle's RC Book expires in ${daysUntilExpiry} days (${expiryDate}). Please renew to maintain vehicle registration validity.`,
        "insurance": `Your vehicle insurance expires in ${daysUntilExpiry} days (${expiryDate}). Renew now to stay protected and legally compliant.`
      };
      const existingNotifications = await storage.getNotificationsByVehicle(vehicleId);
      const existingNotification = existingNotifications.find(
        (n) => n.type === documentType && n.title === titleMap[documentType] && n.message?.includes(`${daysUntilExpiry} days`)
      );
      if (!existingNotification) {
        const notificationData = {
          vehicleId,
          type: documentType,
          title: titleMap[documentType] || "Document Renewal Reminder",
          message: descriptionMap[documentType] || `Your document expires in ${daysUntilExpiry} days. Please renew.`,
          dueDate: expiryDate,
          isRead: false
        };
        await storage.createNotification(notificationData);
        console.log(`Created ${documentType} expiry notification for vehicle ${vehicleId}, expires in ${daysUntilExpiry} days`);
      }
    } catch (error) {
      console.error(`Error creating notification for ${documentType}:`, error);
    }
  }
  // Create notification for expired documents
  async createExpiredNotification(vehicleId, userId, documentType, expiryDate, daysOverdue) {
    try {
      const titleMap = {
        "road_tax": "URGENT: Road Tax Expired!",
        "fitness_certificate": "URGENT: Fitness Certificate Expired!",
        "travel_permits": "URGENT: Travel Permit Expired!",
        "emission": "URGENT: Emission Certificate Expired!",
        "rc_book": "URGENT: RC Book Expired!",
        "insurance": "URGENT: Vehicle Insurance Expired!"
      };
      const descriptionMap = {
        "road_tax": `Your vehicle's Road Tax expired ${daysOverdue} days ago (${expiryDate}). Renew immediately to avoid penalties and legal issues.`,
        "fitness_certificate": `Your vehicle's Fitness Certificate expired ${daysOverdue} days ago (${expiryDate}). Immediate renewal required for legal compliance.`,
        "travel_permits": `Your vehicle's Travel Permit expired ${daysOverdue} days ago (${expiryDate}). Cannot travel until renewed.`,
        "emission": `Your vehicle's Emission Certificate expired ${daysOverdue} days ago (${expiryDate}). Immediate renewal required to avoid violations.`,
        "rc_book": `Your vehicle's RC Book expired ${daysOverdue} days ago (${expiryDate}). Vehicle registration invalid - renew immediately.`,
        "insurance": `Your vehicle insurance expired ${daysOverdue} days ago (${expiryDate}). Driving without insurance is illegal - renew immediately!`
      };
      const existingNotifications = await storage.getNotificationsByVehicle(vehicleId);
      const existingNotification = existingNotifications.find(
        (n) => n.type === documentType && n.title === titleMap[documentType]
      );
      if (!existingNotification) {
        const notificationData = {
          vehicleId,
          type: documentType,
          title: titleMap[documentType] || "URGENT: Document Expired!",
          message: descriptionMap[documentType] || `Your document expired ${daysOverdue} days ago. Renew immediately.`,
          dueDate: expiryDate,
          isRead: false
        };
        await storage.createNotification(notificationData);
        console.log(`Created EXPIRED ${documentType} notification for vehicle ${vehicleId}, expired ${daysOverdue} days ago`);
      }
    } catch (error) {
      console.error(`Error creating expired notification for ${documentType}:`, error);
    }
  }
  // Process document upload and track expiry
  async processDocumentUpload(vehicleId, userId, documentType, expiryDate, amount) {
    try {
      if (!this.expiryDocumentTypes.includes(documentType) || !expiryDate) {
        return;
      }
      const expiryData = {
        vehicleId,
        userId,
        documentType,
        expiryDate,
        amount: amount || null,
        issueDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
        reminderSent: false,
        isActive: true
      };
      await storage.createDocumentExpiry(expiryData);
      console.log(`Processed document upload: ${documentType} for vehicle ${vehicleId}`);
      await this.processExpiryNotifications(vehicleId, userId, documentType, expiryDate);
    } catch (error) {
      console.error(`Error processing document upload:`, error);
    }
  }
};
var documentExpiryService = new DocumentExpiryService();

// server/routes.ts
import crypto from "crypto";
import multer from "multer";
import path from "path";
import fs from "fs";
var appStorageDir = path.join(process.cwd(), "app_storage");
var documentsDir = path.join(appStorageDir, "documents");
var tempDir = path.join(appStorageDir, "temp");
[appStorageDir, documentsDir, tempDir].forEach((dir) => {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
});
var uploadsDir = path.join(process.cwd(), "uploads");
if (!fs.existsSync(uploadsDir)) {
  fs.mkdirSync(uploadsDir, { recursive: true });
}
var upload = multer({
  dest: tempDir,
  // Use temp directory for initial uploads
  limits: {
    fileSize: 10 * 1024 * 1024
    // 10MB limit
  },
  fileFilter: (req, file, cb) => {
    const allowedTypes = ["image/jpeg", "image/png", "image/webp", "application/pdf"];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error("Invalid file type. Only JPEG, PNG, WebP and PDF files are allowed."));
    }
  }
});
function secureFileStorage(tempPath, originalName, category) {
  const fileExtension = path.extname(originalName);
  const timestamp2 = Date.now();
  const randomId = crypto.randomBytes(8).toString("hex");
  const secureFileName = `${category}_${timestamp2}_${randomId}${fileExtension}`;
  const securePath = path.join(documentsDir, secureFileName);
  fs.copyFileSync(tempPath, securePath);
  if (fs.existsSync(tempPath)) {
    fs.unlinkSync(tempPath);
  }
  return `/app_storage/documents/${secureFileName}`;
}
function deleteSecureFile(filePath) {
  try {
    if (filePath.startsWith("/app_storage/documents/")) {
      const fullPath = path.join(process.cwd(), filePath);
      if (fs.existsSync(fullPath)) {
        fs.unlinkSync(fullPath);
        return true;
      }
    }
    if (filePath.startsWith("/uploads/")) {
      const fullPath = path.join(process.cwd(), filePath);
      if (fs.existsSync(fullPath)) {
        fs.unlinkSync(fullPath);
        return true;
      }
    }
    return false;
  } catch (error) {
    console.error("Error deleting file:", error);
    return false;
  }
}
function generateOTP() {
  return Math.floor(1e5 + Math.random() * 9e5).toString();
}
function getOTPExpiry() {
  const expiry = /* @__PURE__ */ new Date();
  expiry.setMinutes(expiry.getMinutes() + 10);
  return expiry;
}
function isValidEmail(email) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}
function isValidMobile(mobile) {
  const cleanMobile = mobile.replace(/\D/g, "");
  return cleanMobile.length >= 10;
}
function maskIdentifier(identifier) {
  if (isValidEmail(identifier)) {
    const [username, domain] = identifier.split("@");
    const maskedUsername = username.slice(0, 2) + "*".repeat(username.length - 2);
    return `${maskedUsername}@${domain}`;
  } else {
    const cleanMobile = identifier.replace(/\D/g, "");
    return cleanMobile.slice(0, 2) + "*".repeat(cleanMobile.length - 4) + cleanMobile.slice(-2);
  }
}
async function registerRoutes(app2) {
  app2.use("/app_storage/documents", (req, res, next) => {
    const filePath = path.join(documentsDir, req.path);
    if (!fs.existsSync(filePath)) {
      return res.status(404).send("File not found");
    }
    const buffer = fs.readFileSync(filePath);
    let contentType = "application/octet-stream";
    if (buffer.length >= 4) {
      const header = buffer.toString("hex", 0, 4);
      if (header.startsWith("ffd8ff")) {
        contentType = "image/jpeg";
      } else if (header.startsWith("89504e47")) {
        contentType = "image/png";
      } else if (header.startsWith("52494646")) {
        contentType = "image/webp";
      } else if (header.startsWith("25504446")) {
        contentType = "application/pdf";
      }
    }
    res.setHeader("Content-Type", contentType);
    res.setHeader("Content-Disposition", "inline");
    res.sendFile(filePath);
  });
  app2.use("/uploads", (req, res, next) => {
    const filePath = path.join(process.cwd(), "uploads", req.path);
    if (!fs.existsSync(filePath)) {
      return res.status(404).send("File not found");
    }
    const buffer = fs.readFileSync(filePath);
    let contentType = "application/octet-stream";
    if (buffer.length >= 4) {
      const header = buffer.toString("hex", 0, 4);
      if (header.startsWith("ffd8ff")) {
        contentType = "image/jpeg";
      } else if (header.startsWith("89504e47")) {
        contentType = "image/png";
      } else if (header.startsWith("52494646")) {
        contentType = "image/webp";
      } else if (header.startsWith("25504446")) {
        contentType = "application/pdf";
      }
    }
    res.setHeader("Content-Type", contentType);
    res.setHeader("Content-Disposition", "inline");
    res.sendFile(filePath);
  });
  app2.post("/api/auth/signin", async (req, res) => {
    try {
      const validatedData = signInSchema.parse(req.body);
      const { identifier } = validatedData;
      const existingUser = await storage.getUserByIdentifier(identifier);
      const otp = generateOTP();
      const type = isValidEmail(identifier) ? "email" : "mobile";
      await storage.createOtpVerification({
        identifier,
        otp,
        type,
        expiresAt: getOTPExpiry(),
        isUsed: false
      });
      console.log(`\u{1F510} OTP for ${identifier}: ${otp}`);
      try {
        const { smsService: smsService2 } = await Promise.resolve().then(() => (init_sms_service(), sms_service_exports));
        await smsService2.sendOTP(identifier, otp);
      } catch (error) {
        console.error("SMS service error:", error);
        console.log("\u{1F4F1} Fallback: OTP logged to console only");
      }
      res.json({
        exists: !!existingUser,
        maskedIdentifier: maskIdentifier(identifier),
        message: `OTP sent to ${maskIdentifier(identifier)}`
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "Invalid request" });
    }
  });
  app2.post("/api/auth/verify-otp", async (req, res) => {
    try {
      console.log("\u{1F50D} SERVER: verify-otp endpoint called");
      console.log("\u{1F50D} SERVER: Raw request body:", req.body);
      console.log("\u{1F50D} SERVER: OTP length:", req.body?.otp?.length);
      console.log("\u{1F50D} SERVER: OTP value:", req.body?.otp);
      console.log("\u{1F50D} SERVER: Identifier:", req.body?.identifier);
      const validatedData = verifyOtpSchema.parse(req.body);
      const { identifier, otp } = validatedData;
      const validOtp = await storage.getValidOtp(identifier, otp);
      if (!validOtp) {
        return res.status(400).json({ message: "Invalid or expired OTP" });
      }
      await storage.markOtpAsUsed(validOtp.id);
      let user = await storage.getUserByIdentifier(identifier);
      console.log(`Debug: Looking for user with identifier: ${identifier}`);
      if (!user) {
        console.log(`Debug: No existing user found, creating new user for: ${identifier}`);
        const userData = {
          username: identifier,
          password: "temp",
          // In real app, this would be handled differently
          email: isValidEmail(identifier) ? identifier : void 0,
          mobile: isValidMobile(identifier) ? identifier : void 0
        };
        user = await storage.createUser(userData);
        console.log(`Debug: Created new user with ID: ${user.id}`);
      } else {
        console.log(`Debug: Found existing user with ID: ${user.id}, PIN: ${user.pin ? "set" : "not set"}`);
      }
      if (user.isBlocked) {
        return res.status(403).json({
          message: "Account access restricted",
          isBlocked: true,
          blockedReason: user.blockedReason || "Account has been blocked by admin",
          contactEmail: "info@arudhih.com"
        });
      }
      await storage.updateUser(user.id, { isVerified: true });
      const profile = await storage.getUserProfile(user.id);
      const hasPin = user.pin !== null && user.pin !== void 0 && user.pin !== "";
      console.log(`Debug: User ${user.id} (${identifier}) - hasProfile: ${!!profile}, hasPin: ${hasPin}`);
      try {
        await documentExpiryService.runExpiryCheckProcess(user.id);
      } catch (error) {
        console.error("Error running document expiry check:", error);
      }
      res.json({
        success: true,
        userId: user.id,
        hasProfile: !!profile,
        hasPin,
        message: "Authentication successful"
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "Verification failed" });
    }
  });
  app2.post("/api/auth/resend-otp", async (req, res) => {
    try {
      const { identifier } = req.body;
      if (!identifier) {
        return res.status(400).json({ message: "Identifier is required" });
      }
      const otp = generateOTP();
      const type = isValidEmail(identifier) ? "email" : "mobile";
      await storage.createOtpVerification({
        identifier,
        otp,
        type,
        expiresAt: getOTPExpiry(),
        isUsed: false
      });
      console.log(`\u{1F510} New OTP for ${identifier}: ${otp}`);
      try {
        const { smsService: smsService2 } = await Promise.resolve().then(() => (init_sms_service(), sms_service_exports));
        await smsService2.sendOTP(identifier, otp);
      } catch (error) {
        console.error("SMS service error:", error);
        console.log("\u{1F4F1} Fallback: OTP logged to console only");
      }
      res.json({
        message: `New OTP sent to ${maskIdentifier(identifier)}`
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "Failed to resend OTP" });
    }
  });
  app2.post("/api/auth/pin-login", async (req, res) => {
    try {
      const validatedData = pinLoginSchema.parse(req.body);
      const { identifier, pin } = validatedData;
      const existingUser = await storage.getUserByIdentifier(identifier);
      if (existingUser && existingUser.isBlocked) {
        return res.status(403).json({
          message: "Account access restricted",
          isBlocked: true,
          blockedReason: existingUser.blockedReason || "Account has been blocked by admin",
          contactEmail: "info@arudhih.com"
        });
      }
      const user = await storage.verifyPin(identifier, pin);
      if (!user) {
        return res.status(400).json({ message: "Invalid PIN or user not found" });
      }
      const profile = await storage.getUserProfile(user.id);
      try {
        await documentExpiryService.runExpiryCheckProcess(user.id);
      } catch (error) {
        console.error("Error running document expiry check:", error);
      }
      res.json({
        success: true,
        userId: user.id,
        hasProfile: !!profile,
        message: "PIN authentication successful"
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "PIN login failed" });
    }
  });
  app2.post("/api/auth/set-pin", async (req, res) => {
    try {
      const validatedData = setPinSchema.parse(req.body);
      const { pin } = validatedData;
      const userId = req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const updatedUser = await storage.updateUserPin(userId, pin);
      if (!updatedUser) {
        return res.status(400).json({ message: "Failed to set PIN" });
      }
      res.json({
        success: true,
        message: "PIN set successfully"
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "Failed to set PIN" });
    }
  });
  app2.post("/api/auth/biometric-setup", async (req, res) => {
    try {
      const validatedData = biometricSetupSchema.parse(req.body);
      const { enabled } = validatedData;
      const userId = req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const updatedUser = await storage.updateBiometricSetting(userId, enabled);
      if (!updatedUser) {
        return res.status(400).json({ message: "Failed to update biometric setting" });
      }
      res.json({
        success: true,
        message: `Biometric authentication ${enabled ? "enabled" : "disabled"} successfully`
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "Failed to update biometric setting" });
    }
  });
  app2.post("/api/auth/reset-pin", async (req, res) => {
    try {
      console.log("\u{1F6A8} SERVER: reset-pin endpoint called");
      console.log("\u{1F6A8} SERVER: req.body:", JSON.stringify(req.body, null, 2));
      console.log("\u{1F6A8} SERVER: req.headers:", JSON.stringify(req.headers, null, 2));
      const { identifier, otp, newPin, confirmPin } = req.body;
      console.log("\u{1F6A8} SERVER: Extracted values:");
      console.log("\u{1F6A8} SERVER: - identifier:", identifier);
      console.log("\u{1F6A8} SERVER: - otp:", otp);
      console.log("\u{1F6A8} SERVER: - newPin:", newPin);
      console.log("\u{1F6A8} SERVER: - confirmPin:", confirmPin);
      if (!identifier || !otp || !newPin || !confirmPin) {
        console.log("\u{1F6A8} SERVER: Missing fields - returning 400");
        return res.status(400).json({ message: "All fields are required" });
      }
      if (newPin !== confirmPin) {
        return res.status(400).json({ message: "PINs do not match" });
      }
      if (newPin.length !== 4 || !/^\d{4}$/.test(newPin)) {
        return res.status(400).json({ message: "PIN must be exactly 4 digits" });
      }
      const otpRecord = await storage.getLatestOtp(identifier);
      if (!otpRecord || otpRecord.otp !== otp || otpRecord.isUsed) {
        return res.status(400).json({ message: "Invalid or expired OTP" });
      }
      if (/* @__PURE__ */ new Date() > otpRecord.expiresAt) {
        return res.status(400).json({ message: "OTP has expired" });
      }
      const user = await storage.getUserByIdentifier(identifier);
      if (!user) {
        return res.status(400).json({ message: "User not found" });
      }
      if (user.isBlocked) {
        return res.status(403).json({
          message: "Account access restricted",
          isBlocked: true,
          blockedReason: user.blockedReason || "Account has been blocked by admin",
          contactEmail: "info@arudhih.com"
        });
      }
      await storage.markOtpAsUsed(otpRecord.id);
      const updatedUser = await storage.updateUserPin(user.id, newPin);
      if (!updatedUser) {
        return res.status(400).json({ message: "Failed to reset PIN" });
      }
      res.json({
        success: true,
        userId: user.id,
        message: "PIN reset successfully"
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "Failed to reset PIN" });
    }
  });
  app2.get("/api/auth/user/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const user = await storage.getUser(userId);
      if (!user) {
        return res.status(404).json({ message: "User not found" });
      }
      res.json({
        id: user.id,
        username: user.username,
        email: user.email,
        mobile: user.mobile,
        hasPin: !!user.pin,
        biometricEnabled: user.biometricEnabled,
        subscriptionStatus: user.subscriptionStatus,
        subscriptionExpiry: user.subscriptionExpiry
      });
    } catch (error) {
      res.status(400).json({ message: error.message || "Failed to fetch user" });
    }
  });
  app2.get("/api/logout", (req, res) => {
    try {
      res.redirect("/sign-in");
    } catch (error) {
      console.error("Logout error:", error);
      res.status(500).json({ message: "Logout failed" });
    }
  });
  app2.get("/api/profile/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const profile = await storage.getUserProfile(userId);
      if (!profile) {
        return res.status(404).json({ message: "Profile not found" });
      }
      res.json(profile);
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch profile" });
    }
  });
  app2.post("/api/profile/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const validatedData = insertUserProfileSchema.parse(req.body);
      const profile = await storage.createUserProfile({ ...validatedData, userId });
      res.status(201).json(profile);
    } catch (error) {
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to create profile" });
      }
    }
  });
  app2.put("/api/profile/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      console.log("Profile update request body:", JSON.stringify(req.body, null, 2));
      const validatedData = insertUserProfileSchema.partial().parse(req.body);
      const profile = await storage.updateUserProfile(userId, validatedData);
      if (!profile) {
        return res.status(404).json({ message: "Profile not found" });
      }
      res.json(profile);
    } catch (error) {
      console.error("Profile update error:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to update profile" });
      }
    }
  });
  app2.get("/api/vehicles", async (req, res) => {
    try {
      const userId = req.query.userId;
      console.log("Debug: /api/vehicles called with userId:", userId);
      if (!userId) {
        console.log("Debug: No userId provided in query");
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        console.log("Debug: Invalid userId format:", userId);
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      console.log("Debug: Fetching vehicles for userId:", parsedUserId);
      const vehicles2 = await storage.getVehicles(parsedUserId);
      console.log("Debug: Found vehicles:", vehicles2.length, "vehicles");
      res.json(vehicles2);
    } catch (error) {
      console.error("Error fetching vehicles:", error);
      res.status(500).json({ message: "Failed to fetch vehicles" });
    }
  });
  app2.get("/api/vehicles/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = req.query.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const vehicle = await storage.getVehicle(id, parsedUserId);
      if (!vehicle) {
        return res.status(404).json({ message: "Vehicle not found" });
      }
      res.json(vehicle);
    } catch (error) {
      console.error("Error fetching vehicle:", error);
      res.status(500).json({ message: "Failed to fetch vehicle" });
    }
  });
  app2.post("/api/vehicles", async (req, res) => {
    try {
      const userId = req.body.userId || 1;
      const user = await storage.getUser(userId);
      const vehicles2 = await storage.getVehicles(userId);
      const vehicleCount = vehicles2.length;
      const vehicleLimit = user?.vehicleLimit || 2;
      if (vehicleCount >= vehicleLimit) {
        const planNames = {
          free: "Free",
          starter: "Starter (\u20B9118/year)",
          premium: "Premium (\u20B9354/year)",
          professional: "Professional (\u20B9590/year)"
        };
        const currentPlan = planNames[user?.subscriptionStatus] || "Free";
        if (user?.subscriptionStatus === "free") {
          return res.status(400).json({
            message: `Vehicle limit reached! ${currentPlan} plan allows maximum ${vehicleLimit} vehicles. Upgrade to add more vehicles:
\u2022 Starter: Up to 4 vehicles (\u20B9118/year)
\u2022 Premium: Up to 8 vehicles (\u20B9354/year)
\u2022 Professional: Up to 15 vehicles (\u20B9590/year)`,
            currentCount: vehicleCount,
            limit: vehicleLimit,
            subscriptionStatus: user?.subscriptionStatus,
            needsUpgrade: true
          });
        } else {
          return res.status(400).json({
            message: `Vehicle limit reached! ${currentPlan} allows up to ${vehicleLimit} vehicles. Upgrade to a higher plan for more vehicles.`,
            currentCount: vehicleCount,
            limit: vehicleLimit,
            subscriptionStatus: user?.subscriptionStatus,
            needsUpgrade: true
          });
        }
      }
      console.log("\u{1F697} Vehicle creation request body:", JSON.stringify(req.body, null, 2));
      const validatedData = insertVehicleSchema.parse(req.body);
      const vehicleWithUser = { ...validatedData, userId };
      const vehicle = await storage.createVehicle(vehicleWithUser);
      res.status(201).json(vehicle);
    } catch (error) {
      console.error("\u{1F6A8} Vehicle creation validation error:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to create vehicle" });
      }
    }
  });
  app2.put("/api/vehicles/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = parseInt(req.query.userId) || req.body.userId || 1;
      if (isNaN(userId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const validatedData = insertVehicleSchema.partial().parse(req.body);
      const vehicle = await storage.updateVehicle(id, userId, validatedData);
      if (!vehicle) {
        return res.status(404).json({ message: "Vehicle not found or you don't have permission to edit it" });
      }
      res.json(vehicle);
    } catch (error) {
      console.error("Error updating vehicle:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to update vehicle" });
      }
    }
  });
  app2.put("/api/vehicles/:id/ocr-data", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = parseInt(req.query.userId) || 1;
      const { ocrPolicyNumber, ocrSumInsured, ocrPremiumAmount, ocrInsuredName } = req.body;
      const vehicle = await storage.updateVehicle(id, userId, {
        ocrPolicyNumber,
        ocrSumInsured,
        ocrPremiumAmount,
        ocrInsuredName
      });
      if (!vehicle) {
        return res.status(404).json({ message: "Vehicle not found" });
      }
      res.json({
        success: true,
        message: "OCR data saved successfully",
        vehicle
      });
    } catch (error) {
      console.error("OCR data update error:", error);
      res.status(500).json({ message: "Failed to save OCR data" });
    }
  });
  app2.delete("/api/vehicles/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = parseInt(req.query.userId) || 1;
      if (isNaN(id)) {
        return res.status(400).json({ message: "Invalid vehicle ID" });
      }
      const deleted = await storage.deleteVehicle(id, userId);
      if (!deleted) {
        return res.status(404).json({ message: "Vehicle not found" });
      }
      res.status(204).send();
    } catch (error) {
      console.error("Delete vehicle error:", error);
      res.status(500).json({ message: "Failed to delete vehicle" });
    }
  });
  app2.post("/api/upload", upload.single("file"), async (req, res) => {
    try {
      if (!req.file) {
        return res.status(400).json({ message: "No file uploaded" });
      }
      const { type } = req.body;
      const secureFilePath = secureFileStorage(req.file.path, req.file.originalname, type || "general");
      console.log(`File uploaded successfully to secure storage: ${req.file.originalname}`);
      res.json({
        fileName: req.file.originalname,
        filePath: secureFilePath,
        fileSize: req.file.size,
        mimeType: req.file.mimetype,
        type: type || "other"
      });
    } catch (error) {
      console.error("Upload error:", error);
      res.status(500).json({ message: "Failed to upload file", error: error.message });
    }
  });
  app2.post("/api/documents", async (req, res) => {
    try {
      const documentData = req.body;
      console.log("Creating document with data:", documentData);
      const vehicle = await storage.getVehicle(documentData.vehicleId, 1);
      if (!vehicle) {
        return res.status(404).json({ message: "Vehicle not found" });
      }
      const validatedData = insertDocumentSchema.parse(documentData);
      const document = await storage.createDocument(validatedData);
      console.log("Document created successfully:", document);
      res.status(201).json(document);
    } catch (error) {
      console.error("Error creating document:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to create document" });
      }
    }
  });
  app2.get("/api/vehicles/:vehicleId/documents", async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const documents2 = await storage.getDocumentsByVehicle(vehicleId);
      res.json(documents2);
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch documents" });
    }
  });
  app2.post("/api/vehicles/:vehicleId/documents", upload.single("file"), async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const { type } = req.body;
      if (!req.file) {
        return res.status(400).json({ message: "No file uploaded" });
      }
      const vehicle = await storage.getVehicle(vehicleId, 1);
      if (!vehicle) {
        return res.status(404).json({ message: "Vehicle not found" });
      }
      const secureFilePath = secureFileStorage(req.file.path, req.file.originalname, `doc_v${vehicleId}_${type}`);
      const documentData = {
        vehicleId,
        type: type || "other",
        fileName: req.file.originalname,
        filePath: secureFilePath,
        fileSize: req.file.size,
        mimeType: req.file.mimetype
      };
      const validatedData = insertDocumentSchema.parse(documentData);
      const document = await storage.createDocument(validatedData);
      res.status(201).json(document);
    } catch (error) {
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to upload document" });
      }
    }
  });
  app2.get("/api/documents/:id/file", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const document = await storage.getDocument(id);
      if (!document) {
        return res.status(404).json({ message: "Document not found" });
      }
      if (!fs.existsSync(document.filePath)) {
        return res.status(404).json({ message: "File not found" });
      }
      res.setHeader("Content-Type", document.mimeType);
      res.setHeader("Content-Disposition", `inline; filename="${document.fileName}"`);
      const fileStream = fs.createReadStream(document.filePath);
      fileStream.pipe(res);
    } catch (error) {
      res.status(500).json({ message: "Failed to serve file" });
    }
  });
  app2.delete("/api/documents/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const document = await storage.getDocument(id);
      if (!document) {
        return res.status(404).json({ message: "Document not found" });
      }
      const deleted = await storage.deleteDocument(id);
      if (!deleted) {
        return res.status(404).json({ message: "Document not found" });
      }
      if (document.filePath) {
        deleteSecureFile(document.filePath);
      }
      res.status(204).send();
    } catch (error) {
      console.error("Error deleting document:", error);
      res.status(500).json({ message: "Failed to delete document" });
    }
  });
  app2.get("/api/stats", async (req, res) => {
    try {
      const userId = req.query.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const vehicles2 = await storage.getVehicles(parsedUserId);
      const currentDate = /* @__PURE__ */ new Date();
      const thirtyDaysFromNow = /* @__PURE__ */ new Date();
      thirtyDaysFromNow.setDate(currentDate.getDate() + 30);
      let expiringSoon = 0;
      let expired = 0;
      vehicles2.forEach((vehicle) => {
        const insuranceExpiry = vehicle.insuranceExpiry ? new Date(vehicle.insuranceExpiry) : null;
        const emissionExpiry = vehicle.emissionExpiry ? new Date(vehicle.emissionExpiry) : null;
        const isExpired = insuranceExpiry && insuranceExpiry < currentDate || emissionExpiry && emissionExpiry < currentDate;
        const isExpiringSoon = insuranceExpiry && insuranceExpiry >= currentDate && insuranceExpiry <= thirtyDaysFromNow || emissionExpiry && emissionExpiry >= currentDate && emissionExpiry <= thirtyDaysFromNow;
        if (isExpired) {
          expired++;
        } else if (isExpiringSoon) {
          expiringSoon++;
        }
      });
      res.json({
        totalVehicles: vehicles2.length,
        expiringSoon,
        expired
      });
    } catch (error) {
      res.status(500).json({ message: "Failed to fetch stats" });
    }
  });
  app2.post("/api/test-notifications", async (req, res) => {
    try {
      const userId = req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      console.log(`Manually triggering document expiry check for user ${userId}`);
      await documentExpiryService.runExpiryCheckProcess(userId);
      res.json({ message: "Notification check triggered successfully" });
    } catch (error) {
      console.error("Error triggering notifications:", error);
      res.status(500).json({ message: "Failed to trigger notifications" });
    }
  });
  app2.get("/api/notifications", async (req, res) => {
    try {
      const userId = req.query.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const userVehicles = await storage.getVehicles(parsedUserId);
      if (userVehicles.length === 0) {
        return res.json([]);
      }
      const notifications2 = await storage.getNotifications(parsedUserId);
      res.json(notifications2);
    } catch (error) {
      console.error("Error fetching notifications:", error);
      res.status(500).json({ error: "Failed to fetch notifications" });
    }
  });
  app2.post("/api/notifications/generate", async (req, res) => {
    try {
      const userId = req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      await storage.generateRenewalNotifications(parsedUserId);
      res.json({ success: true, message: "Notifications generated successfully" });
    } catch (error) {
      console.error("Error generating notifications:", error);
      res.status(500).json({ error: "Failed to generate notifications" });
    }
  });
  app2.put("/api/notifications/:id/read", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      await storage.markNotificationAsRead(id);
      res.json({ success: true });
    } catch (error) {
      console.error("Error marking notification as read:", error);
      res.status(500).json({ error: "Failed to mark notification as read" });
    }
  });
  app2.get("/api/emergency-contacts/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const contact = await storage.getEmergencyContact(userId);
      res.json(contact || {});
    } catch (error) {
      console.error("Error fetching emergency contact:", error);
      res.status(500).json({ error: "Failed to fetch emergency contact" });
    }
  });
  app2.post("/api/emergency-contacts/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const contactData = insertEmergencyContactSchema.parse(req.body);
      const contact = await storage.createEmergencyContact({ ...contactData, userId });
      res.json(contact);
    } catch (error) {
      console.error("Error creating emergency contact:", error);
      res.status(500).json({ error: "Failed to create emergency contact" });
    }
  });
  app2.put("/api/emergency-contacts/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const contactData = insertEmergencyContactSchema.partial().parse(req.body);
      const contact = await storage.updateEmergencyContact(userId, contactData);
      if (contact) {
        res.json(contact);
      } else {
        res.status(404).json({ error: "Emergency contact not found" });
      }
    } catch (error) {
      console.error("Error updating emergency contact:", error);
      res.status(500).json({ error: "Failed to update emergency contact" });
    }
  });
  app2.get("/api/vehicles/:vehicleId/violations", async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const violations = await storage.getTrafficViolations(vehicleId);
      res.json(violations);
    } catch (error) {
      console.error("Error fetching traffic violations:", error);
      res.status(500).json({ error: "Failed to fetch traffic violations" });
    }
  });
  app2.post("/api/vehicles/:vehicleId/check-violations", async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const vehicle = await storage.getVehicle(vehicleId, 1);
      if (!vehicle) {
        return res.status(404).json({ error: "Vehicle not found" });
      }
      console.log(`\u{1F697} Checking violations for vehicle ${vehicle.licensePlate}`);
      await storage.clearVehicleViolations(vehicleId);
      const violations = await trafficViolationService.checkViolations(vehicle.licensePlate);
      for (const violation of violations) {
        await storage.createTrafficViolation({
          vehicleId,
          challanNumber: violation.challanNumber,
          offense: violation.offense,
          fineAmount: violation.fineAmount,
          violationDate: violation.violationDate,
          location: violation.location,
          status: violation.status,
          paymentDate: violation.paymentDate || null,
          lastChecked: /* @__PURE__ */ new Date()
        });
      }
      console.log(`\u2705 Found ${violations.length} violations for ${vehicle.licensePlate}`);
      const stateCode = vehicle.licensePlate.substring(0, 2);
      const paymentUrl = trafficViolationService.getPaymentUrl(vehicle.licensePlate);
      const message = violations.length > 0 ? `Traffic violations checked successfully. Found ${violations.length} violations.` : `No violations found for ${vehicle.licensePlate}. Clean record!`;
      res.json({
        message,
        violations,
        stateCode,
        paymentUrl,
        source: violations.length > 0 ? `${stateCode} State Government API` : `${stateCode} State Government API (No violations found)`
      });
    } catch (error) {
      console.error("Error checking traffic violations:", error);
      res.status(500).json({
        error: error.message || "Failed to check traffic violations",
        details: "Please ensure vehicle number is valid and state API is accessible"
      });
    }
  });
  app2.put("/api/violations/:violationId/status", async (req, res) => {
    try {
      const violationId = parseInt(req.params.violationId);
      const { status, paymentDate } = req.body;
      const violation = await storage.updateTrafficViolationStatus(violationId, status, paymentDate);
      if (!violation) {
        return res.status(404).json({ error: "Traffic violation not found" });
      }
      res.json(violation);
    } catch (error) {
      console.error("Error updating violation status:", error);
      res.status(500).json({ error: "Failed to update violation status" });
    }
  });
  app2.get("/api/maintenance/records/:vehicleId", async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const records = await storage.getMaintenanceRecords(vehicleId);
      res.json(records);
    } catch (error) {
      console.error("Error fetching maintenance records:", error);
      res.status(500).json({ error: "Failed to fetch maintenance records" });
    }
  });
  app2.post("/api/maintenance/records", upload.fields([{ name: "warrantyCard" }, { name: "invoice" }]), async (req, res) => {
    try {
      const files = req.files;
      const recordData = insertMaintenanceRecordSchema.parse({
        vehicleId: parseInt(req.body.vehicleId),
        maintenanceType: req.body.maintenanceType,
        completedDate: req.body.completedDate || null,
        notes: req.body.notes || null
      });
      let warrantyCardPath = null;
      let invoicePath = null;
      if (files?.warrantyCard?.[0]) {
        const file = files.warrantyCard[0];
        warrantyCardPath = secureFileStorage(file.path, file.originalname, `warranty_v${recordData.vehicleId}`);
      }
      if (files?.invoice?.[0]) {
        const file = files.invoice[0];
        invoicePath = secureFileStorage(file.path, file.originalname, `invoice_v${recordData.vehicleId}`);
      }
      const record = await storage.createMaintenanceRecord({
        ...recordData,
        warrantyCardPath,
        invoicePath
      });
      if (req.body.speedometerReading) {
        await storage.updateVehicleSpeedometerReading(recordData.vehicleId, parseInt(req.body.speedometerReading));
      }
      res.json(record);
    } catch (error) {
      console.error("Error creating maintenance record:", error);
      res.status(500).json({ error: "Failed to create maintenance record" });
    }
  });
  app2.put("/api/maintenance/records/:id", upload.fields([{ name: "warrantyCard" }, { name: "invoice" }]), async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const files = req.files;
      const updateData = {
        completedDate: req.body.completedDate || null,
        notes: req.body.notes || null
      };
      if (files?.warrantyCard?.[0]) {
        const file = files.warrantyCard[0];
        updateData.warrantyCardPath = secureFileStorage(file.path, file.originalname, `warranty_r${id}`);
      }
      if (files?.invoice?.[0]) {
        const file = files.invoice[0];
        updateData.invoicePath = secureFileStorage(file.path, file.originalname, `invoice_r${id}`);
      }
      const record = await storage.updateMaintenanceRecord(id, updateData);
      res.json(record);
    } catch (error) {
      console.error("Error updating maintenance record:", error);
      res.status(500).json({ error: "Failed to update maintenance record" });
    }
  });
  app2.delete("/api/maintenance/records/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const record = await storage.getMaintenanceRecord(id);
      if (!record) {
        return res.status(404).json({ message: "Maintenance record not found" });
      }
      const deleted = await storage.deleteMaintenanceRecord(id);
      if (!deleted) {
        return res.status(404).json({ message: "Maintenance record not found" });
      }
      if (record.warrantyCardPath) {
        deleteSecureFile(record.warrantyCardPath);
      }
      if (record.invoicePath) {
        deleteSecureFile(record.invoicePath);
      }
      res.json({ message: "Maintenance record deleted successfully" });
    } catch (error) {
      console.error("Error deleting maintenance record:", error);
      res.status(500).json({ message: "Failed to delete maintenance record" });
    }
  });
  app2.get("/api/service-logs/:vehicleId", async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const logs = await storage.getServiceLogs(vehicleId);
      res.json(logs);
    } catch (error) {
      console.error("Error fetching service logs:", error);
      res.status(500).json({ message: "Failed to fetch service logs" });
    }
  });
  app2.post("/api/service-logs", upload.single("invoice"), async (req, res) => {
    try {
      const data = req.body;
      let invoicePath = null;
      if (req.file) {
        invoicePath = secureFileStorage(req.file.path, req.file.originalname, `service_v${data.vehicleId}`);
      }
      const serviceLog = await storage.createServiceLog({
        vehicleId: parseInt(data.vehicleId),
        serviceType: data.serviceType.toUpperCase(),
        serviceDate: data.serviceDate,
        serviceCentre: data.serviceCentre.toUpperCase(),
        currentSpeedometerReading: data.currentSpeedometerReading ? parseInt(data.currentSpeedometerReading) : null,
        billAmount: data.billAmount ? parseInt(data.billAmount) : null,
        // billAmount is in paise
        serviceIntervalMonths: data.serviceIntervalMonths ? parseInt(data.serviceIntervalMonths) : null,
        notes: data.notes || null,
        invoicePath
      });
      if (data.currentSpeedometerReading) {
        await storage.updateVehicleSpeedometerReading(parseInt(data.vehicleId), parseInt(data.currentSpeedometerReading));
      }
      res.status(201).json(serviceLog);
    } catch (error) {
      console.error("Error creating service log:", error);
      res.status(500).json({ message: "Failed to create service log" });
    }
  });
  app2.put("/api/service-logs/:id", upload.single("invoice"), async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const data = req.body;
      const updateData = {
        serviceType: data.serviceType?.toUpperCase(),
        serviceDate: data.serviceDate,
        serviceCentre: data.serviceCentre?.toUpperCase(),
        billAmount: data.billAmount ? parseInt(data.billAmount) : null,
        // billAmount is in paise
        serviceIntervalMonths: data.serviceIntervalMonths ? parseInt(data.serviceIntervalMonths) : null,
        notes: data.notes || null
      };
      if (req.file) {
        updateData.invoicePath = secureFileStorage(req.file.path, req.file.originalname, `service_log${id}`);
      }
      const serviceLog = await storage.updateServiceLog(id, updateData);
      if (!serviceLog) {
        return res.status(404).json({ message: "Service log not found" });
      }
      res.json(serviceLog);
    } catch (error) {
      console.error("Error updating service log:", error);
      res.status(500).json({ message: "Failed to update service log" });
    }
  });
  app2.delete("/api/service-logs/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const serviceLog = await storage.getServiceLog(id);
      if (!serviceLog) {
        return res.status(404).json({ message: "Service log not found" });
      }
      const deleted = await storage.deleteServiceLog(id);
      if (!deleted) {
        return res.status(404).json({ message: "Service log not found" });
      }
      if (serviceLog.invoicePath) {
        deleteSecureFile(serviceLog.invoicePath);
      }
      res.json({ message: "Service log deleted successfully" });
    } catch (error) {
      console.error("Error deleting service log:", error);
      res.status(500).json({ message: "Failed to delete service log" });
    }
  });
  app2.post("/api/create-subscription", async (req, res) => {
    try {
      const razorpayKeyId = process.env.RAZORPAY_KEY_ID || "rzp_test_YZwlPCt1kjh0Hj";
      const razorpayKeySecret = process.env.RAZORPAY_KEY_SECRET || "ByVSIcrxqlDBKHOtp9JC39Py";
      console.log("Razorpay Config Check:", {
        envKeyId: process.env.RAZORPAY_KEY_ID ? "set" : "missing",
        envKeySecret: process.env.RAZORPAY_KEY_SECRET ? "set" : "missing",
        finalKeyId: razorpayKeyId ? `${razorpayKeyId.slice(0, 12)}...` : "missing",
        finalKeySecret: razorpayKeySecret ? "configured" : "missing"
      });
      const { userId, planType = "premium" } = req.body;
      const plans = {
        starter: { amount: 11800, name: "Starter Plan", vehicleLimit: 4 },
        // Rs 118 (100 + 18% GST)
        premium: { amount: 35400, name: "Premium Plan", vehicleLimit: 8 },
        // Rs 354 (300 + 18% GST)
        professional: { amount: 59e3, name: "Professional Plan", vehicleLimit: 15 }
        // Rs 590 (500 + 18% GST)
      };
      const selectedPlan = plans[planType] || plans.premium;
      try {
        const Razorpay = (await import("razorpay")).default;
        const razorpay = new Razorpay({
          key_id: razorpayKeyId,
          key_secret: razorpayKeySecret
        });
        const order = await razorpay.orders.create({
          amount: selectedPlan.amount,
          currency: "INR",
          receipt: `subscription_${userId}_${Date.now()}`,
          notes: {
            userId: userId.toString(),
            planType,
            subscriptionType: "annual"
          }
        });
        res.json({
          orderId: order.id,
          amount: order.amount,
          currency: order.currency,
          key: razorpayKeyId,
          planName: selectedPlan.name
        });
      } catch (razorpayError) {
        console.error("Razorpay API error:", razorpayError);
        const mockOrderId = `order_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        res.json({
          orderId: mockOrderId,
          amount: selectedPlan.amount,
          currency: "INR",
          key: razorpayKeyId,
          planName: selectedPlan.name,
          isMockOrder: true
        });
      }
    } catch (error) {
      console.error("Error creating subscription order:", error);
      res.status(500).json({ error: "Failed to create subscription order" });
    }
  });
  app2.post("/api/verify-subscription", async (req, res) => {
    try {
      const { razorpay_order_id, razorpay_payment_id, razorpay_signature, userId } = req.body;
      const razorpayKeySecret = process.env.RAZORPAY_KEY_SECRET || "ByVSIcrxqlDBKHOtp9JC39Py";
      const razorpayKeyId = process.env.RAZORPAY_KEY_ID || "rzp_test_YZwlPCt1kjh0Hj";
      if (!razorpayKeySecret) {
        return res.status(500).json({ error: "Razorpay secret key not configured" });
      }
      if (!userId) {
        return res.status(400).json({ error: "User ID is required" });
      }
      const generatedSignature = crypto.createHmac("sha256", razorpayKeySecret).update(razorpay_order_id + "|" + razorpay_payment_id).digest("hex");
      const isSignatureValid = generatedSignature === razorpay_signature;
      if (isSignatureValid) {
        try {
          const Razorpay = (await import("razorpay")).default;
          const razorpay = new Razorpay({
            key_id: razorpayKeyId,
            key_secret: razorpayKeySecret
          });
          const payment = await razorpay.payments.fetch(razorpay_payment_id);
          const order = await razorpay.orders.fetch(razorpay_order_id);
          const subscriptionExpiry = /* @__PURE__ */ new Date();
          subscriptionExpiry.setFullYear(subscriptionExpiry.getFullYear() + 1);
          const vehicleLimits = {
            starter: 4,
            premium: 8,
            professional: 15
          };
          const planFromOrder = order.notes?.planType || "premium";
          const vehicleLimit = vehicleLimits[planFromOrder] || 8;
          await storage.updateUser(userId, {
            subscriptionStatus: planFromOrder,
            subscriptionExpiry,
            vehicleLimit,
            razorpayOrderId: razorpay_order_id,
            razorpayPaymentId: razorpay_payment_id
          });
          res.json({
            success: true,
            message: "Subscription activated successfully",
            subscriptionExpiry: subscriptionExpiry.toISOString()
          });
        } catch (razorpayError) {
          console.error("Razorpay API error:", razorpayError);
          const subscriptionExpiry = /* @__PURE__ */ new Date();
          subscriptionExpiry.setFullYear(subscriptionExpiry.getFullYear() + 1);
          await storage.updateUser(userId, {
            subscriptionStatus: "premium",
            subscriptionExpiry,
            vehicleLimit: 8,
            razorpayOrderId: razorpay_order_id,
            razorpayPaymentId: razorpay_payment_id
          });
          res.json({
            success: true,
            message: "Subscription activated successfully (development mode)",
            subscriptionExpiry: subscriptionExpiry.toISOString()
          });
        }
      } else {
        res.status(400).json({ error: "Invalid payment signature" });
      }
    } catch (error) {
      console.error("Error verifying subscription payment:", error);
      res.status(500).json({ error: "Failed to verify payment" });
    }
  });
  app2.get("/api/maintenance/schedule", async (req, res) => {
    try {
      const { make, model, year, drivingCondition = "normal" } = req.query;
      if (!make || !model || !year) {
        return res.status(400).json({
          message: "Missing required parameters: make, model, year"
        });
      }
      const schedule = await maintenanceService.getMaintenanceSchedule(
        make,
        model,
        parseInt(year),
        drivingCondition
      );
      if (!schedule) {
        return res.status(404).json({
          message: "Maintenance schedule not found for this vehicle"
        });
      }
      res.json(schedule);
    } catch (error) {
      console.error("Error fetching maintenance schedule:", error);
      res.status(500).json({ message: "Failed to fetch maintenance schedule" });
    }
  });
  app2.get("/api/maintenance/available", async (req, res) => {
    try {
      const availableSchedules = maintenanceService.getAvailableMaintenance();
      res.json(availableSchedules);
    } catch (error) {
      console.error("Error fetching available maintenance schedules:", error);
      res.status(500).json({ message: "Failed to fetch available maintenance schedules" });
    }
  });
  app2.get("/api/traffic-violations/supported-states", async (req, res) => {
    try {
      const supportedStates = trafficViolationService.getSupportedStates();
      res.json(supportedStates);
    } catch (error) {
      console.error("Error fetching supported states:", error);
      res.status(500).json({ message: "Failed to fetch supported states" });
    }
  });
  app2.get("/api/traffic-violations/payment-url/:licensePlate", async (req, res) => {
    try {
      const licensePlate = req.params.licensePlate;
      const paymentUrl = trafficViolationService.getPaymentUrl(licensePlate);
      const stateCode = licensePlate.substring(0, 2);
      res.json({
        paymentUrl,
        stateCode,
        message: `Payment URL for ${stateCode} state`
      });
    } catch (error) {
      console.error("Error getting payment URL:", error);
      res.status(500).json({ message: "Failed to get payment URL" });
    }
  });
  app2.get("/api/news", async (req, res) => {
    try {
      const news = await newsService.fetchLatestNews();
      res.json(news);
    } catch (error) {
      console.error("Error fetching news:", error);
      res.status(500).json({ message: "Failed to fetch news" });
    }
  });
  app2.get("/api/news/cache-info", async (req, res) => {
    try {
      const cacheInfo = newsService.getCacheInfo();
      res.json(cacheInfo);
    } catch (error) {
      console.error("Error fetching cache info:", error);
      res.status(500).json({ message: "Failed to fetch cache info" });
    }
  });
  app2.post("/api/news/refresh", async (req, res) => {
    try {
      const news = await newsService.fetchLatestNews();
      res.json({
        message: "Real-time news fetched from free government APIs",
        news,
        source: "API Setu, Open Government Data India, Ministry Direct Sources"
      });
    } catch (error) {
      console.error("Error refreshing news:", error);
      res.status(500).json({ message: "Failed to refresh news" });
    }
  });
  app2.get("/api/dashboard/widgets/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      let widgets = await storage.getDashboardWidgets(userId);
      if (widgets.length === 0) {
        widgets = await storage.initializeDefaultWidgets(userId);
      }
      res.json(widgets);
    } catch (error) {
      console.error("Error fetching dashboard widgets:", error);
      res.status(500).json({ message: "Failed to fetch dashboard widgets" });
    }
  });
  app2.post("/api/dashboard/widgets", async (req, res) => {
    try {
      const userId = req.headers["x-user-id"] ? parseInt(req.headers["x-user-id"]) : req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const widgetData = { ...req.body, userId };
      const widget = await storage.createDashboardWidget(widgetData);
      res.json(widget);
    } catch (error) {
      console.error("Error creating dashboard widget:", error);
      res.status(500).json({ message: "Failed to create dashboard widget" });
    }
  });
  app2.patch("/api/dashboard/widgets/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = req.headers["x-user-id"] ? parseInt(req.headers["x-user-id"]) : req.body.userId;
      const updateData = { ...req.body };
      if (userId) {
        updateData.userId = userId;
      }
      const widget = await storage.updateDashboardWidget(id, updateData);
      res.json(widget);
    } catch (error) {
      console.error("Error updating dashboard widget:", error);
      res.status(500).json({ message: "Failed to update dashboard widget" });
    }
  });
  app2.delete("/api/dashboard/widgets/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      await storage.deleteDashboardWidget(id);
      res.json({ success: true });
    } catch (error) {
      console.error("Error deleting dashboard widget:", error);
      res.status(500).json({ message: "Failed to delete dashboard widget" });
    }
  });
  app2.post("/api/ratings", async (req, res) => {
    try {
      const userId = 1;
      const user = await storage.getUser(userId);
      const profile = await storage.getUserProfile(userId);
      if (!user || !profile) {
        return res.status(401).json({ message: "User not found" });
      }
      const ratingData = {
        ...req.body,
        userId,
        userName: profile.name,
        phoneNumber: user.mobile || profile.alternatePhone || "",
        emailId: profile.email
      };
      const validatedData = insertRatingSchema.parse(ratingData);
      const rating = await storage.createRating(validatedData);
      res.status(201).json(rating);
    } catch (error) {
      console.error("Error creating rating:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to create rating" });
      }
    }
  });
  app2.get("/api/ratings", async (req, res) => {
    try {
      const ratings2 = await storage.getRatings();
      res.json(ratings2);
    } catch (error) {
      console.error("Error fetching ratings:", error);
      res.status(500).json({ message: "Failed to fetch ratings" });
    }
  });
  app2.get("/api/ratings/user/:userId", async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const ratings2 = await storage.getUserRatings(userId);
      res.json(ratings2);
    } catch (error) {
      console.error("Error fetching user ratings:", error);
      res.status(500).json({ message: "Failed to fetch user ratings" });
    }
  });
  app2.get("/api/document-expiries/:vehicleId", async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const expiries = await storage.getDocumentExpiries(vehicleId);
      res.json(expiries);
    } catch (error) {
      console.error("Error fetching document expiries:", error);
      res.status(500).json({ message: "Failed to fetch document expiries" });
    }
  });
  app2.post("/api/document-expiries", async (req, res) => {
    try {
      const data = req.body;
      const expiry = await storage.createDocumentExpiry({
        vehicleId: parseInt(data.vehicleId),
        userId: parseInt(data.userId),
        documentType: data.documentType,
        expiryDate: data.expiryDate,
        amount: data.amount ? parseInt(data.amount) : null,
        issueDate: data.issueDate || null,
        reminderSent: false,
        isActive: true
      });
      try {
        await documentExpiryService.processDocumentUpload(
          parseInt(data.vehicleId),
          parseInt(data.userId),
          data.documentType,
          data.expiryDate,
          data.amount ? parseInt(data.amount) : void 0
        );
        console.log(`\u2705 Document expiry processing completed for ${data.documentType}`);
      } catch (processingError) {
        console.error(`\u274C Document expiry processing failed for ${data.documentType}:`, processingError);
      }
      res.status(201).json(expiry);
    } catch (error) {
      console.error("Error creating document expiry:", error);
      res.status(500).json({ message: "Failed to create document expiry" });
    }
  });
  app2.put("/api/document-expiries/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const data = req.body;
      const expiry = await storage.updateDocumentExpiry(id, {
        expiryDate: data.expiryDate,
        amount: data.amount ? parseInt(data.amount) : null,
        issueDate: data.issueDate,
        isActive: data.isActive
      });
      if (!expiry) {
        return res.status(404).json({ message: "Document expiry not found" });
      }
      res.json(expiry);
    } catch (error) {
      console.error("Error updating document expiry:", error);
      res.status(500).json({ message: "Failed to update document expiry" });
    }
  });
  app2.get("/api/service-alerts/:vehicleId", async (req, res) => {
    try {
      const vehicleId = parseInt(req.params.vehicleId);
      const alerts = await storage.getServiceAlerts(vehicleId);
      res.json(alerts);
    } catch (error) {
      console.error("Error fetching service alerts:", error);
      res.status(500).json({ message: "Failed to fetch service alerts" });
    }
  });
  app2.post("/api/service-alerts", async (req, res) => {
    try {
      const data = req.body;
      const alert = await storage.createServiceAlert({
        vehicleId: parseInt(data.vehicleId),
        eventName: data.eventName,
        scheduledDate: data.scheduledDate,
        notes: data.notes || null,
        isActive: true,
        isCompleted: false,
        notificationSent: false
      });
      res.status(201).json(alert);
    } catch (error) {
      console.error("Error creating service alert:", error);
      res.status(500).json({ message: "Failed to create service alert" });
    }
  });
  app2.put("/api/service-alerts/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const data = req.body;
      const alert = await storage.updateServiceAlert(id, {
        eventName: data.eventName,
        scheduledDate: data.scheduledDate,
        notes: data.notes,
        isActive: data.isActive,
        isCompleted: data.isCompleted
      });
      if (!alert) {
        return res.status(404).json({ message: "Service alert not found" });
      }
      res.json(alert);
    } catch (error) {
      console.error("Error updating service alert:", error);
      res.status(500).json({ message: "Failed to update service alert" });
    }
  });
  app2.delete("/api/service-alerts/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const deleted = await storage.deleteServiceAlert(id);
      if (!deleted) {
        return res.status(404).json({ message: "Service alert not found" });
      }
      res.json({ message: "Service alert deleted successfully" });
    } catch (error) {
      console.error("Error deleting service alert:", error);
      res.status(500).json({ message: "Failed to delete service alert" });
    }
  });
  app2.post("/api/service-alerts/check-notifications", async (req, res) => {
    try {
      const activeAlerts = await storage.getActiveServiceAlerts();
      const today = /* @__PURE__ */ new Date();
      const tomorrow = new Date(today);
      tomorrow.setDate(tomorrow.getDate() + 1);
      let notificationsCreated = 0;
      for (const alert of activeAlerts) {
        const scheduledDate = new Date(alert.scheduledDate);
        if (scheduledDate.toDateString() === tomorrow.toDateString() && !alert.notificationSent) {
          await storage.createNotification({
            vehicleId: alert.vehicleId,
            type: "service",
            title: "Service Alert Reminder",
            message: `Reminder: ${alert.eventName} is scheduled for tomorrow (${scheduledDate.toLocaleDateString()})`,
            dueDate: alert.scheduledDate,
            isRead: false
          });
          await storage.updateServiceAlert(alert.id, { notificationSent: true });
          notificationsCreated++;
        }
      }
      res.json({
        success: true,
        message: `${notificationsCreated} service alert notifications created`,
        count: notificationsCreated
      });
    } catch (error) {
      console.error("Error checking service alert notifications:", error);
      res.status(500).json({ message: "Failed to check service alert notifications" });
    }
  });
  app2.get("/api/broadcasts", async (req, res) => {
    try {
      const userId = req.query.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const userVehicles = await storage.getVehicles(parsedUserId);
      if (userVehicles.length === 0) {
        return res.json([]);
      }
      const broadcasts2 = await storage.getBroadcasts();
      res.json(broadcasts2);
    } catch (error) {
      console.error("Error fetching broadcasts:", error);
      res.status(500).json({ message: "Failed to fetch broadcasts" });
    }
  });
  app2.get("/api/broadcasts/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const broadcast = await storage.getBroadcast(id);
      if (!broadcast) {
        return res.status(404).json({ message: "Broadcast not found" });
      }
      await storage.incrementBroadcastViews(id);
      res.json(broadcast);
    } catch (error) {
      console.error("Error fetching broadcast:", error);
      res.status(500).json({ message: "Failed to fetch broadcast" });
    }
  });
  app2.post("/api/broadcasts", async (req, res) => {
    try {
      console.log("Received broadcast request:", req.body);
      const userId = req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const activePostCount = await storage.getUserActiveBroadcastCount(parsedUserId);
      if (activePostCount >= 3) {
        return res.status(400).json({
          message: "You cannot have more than 3 active posts. Please wait for existing posts to expire or delete some posts to create new ones."
        });
      }
      const expiresAt = /* @__PURE__ */ new Date();
      expiresAt.setDate(expiresAt.getDate() + 7);
      const broadcastData = {
        userId: parsedUserId,
        type: req.body.type || "general",
        title: req.body.title,
        description: req.body.description,
        vehicleId: req.body.vehicleId || null,
        price: req.body.price || null,
        contactPhone: req.body.contactPhone,
        contactEmail: req.body.contactEmail || null,
        location: req.body.location || null,
        status: "active",
        viewCount: 0,
        isActive: true,
        expiresAt
      };
      console.log("Creating broadcast with processed data:", broadcastData);
      const broadcast = await storage.createBroadcast(broadcastData);
      res.status(201).json(broadcast);
    } catch (error) {
      console.error("Error creating broadcast:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to create broadcast" });
      }
    }
  });
  app2.put("/api/broadcasts/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const broadcastData = insertBroadcastSchema.partial().parse(req.body);
      const broadcast = await storage.updateBroadcast(id, broadcastData);
      if (!broadcast) {
        return res.status(404).json({ message: "Broadcast not found" });
      }
      res.json(broadcast);
    } catch (error) {
      console.error("Error updating broadcast:", error);
      res.status(500).json({ message: "Failed to update broadcast" });
    }
  });
  app2.delete("/api/broadcasts/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = parseInt(req.query.userId) || 1;
      const broadcast = await storage.getBroadcast(id);
      if (!broadcast) {
        return res.status(404).json({ message: "Broadcast not found" });
      }
      if (broadcast.userId !== userId) {
        return res.status(403).json({ message: "You can only delete your own posts" });
      }
      const deleted = await storage.deleteBroadcast(id, userId);
      if (!deleted) {
        return res.status(404).json({ message: "Broadcast not found" });
      }
      res.status(204).send();
    } catch (error) {
      console.error("Error deleting broadcast:", error);
      res.status(500).json({ message: "Failed to delete broadcast" });
    }
  });
  app2.get("/api/broadcasts/type/:type", async (req, res) => {
    try {
      const type = req.params.type;
      const broadcasts2 = await storage.getBroadcastsByType(type);
      res.json(broadcasts2);
    } catch (error) {
      console.error("Error fetching broadcasts by type:", error);
      res.status(500).json({ message: "Failed to fetch broadcasts by type" });
    }
  });
  setInterval(async () => {
    try {
      await storage.cleanupExpiredBroadcasts();
    } catch (error) {
      console.error("Error during automatic broadcast cleanup:", error);
    }
  }, 24 * 60 * 60 * 1e3);
  setTimeout(async () => {
    try {
      await storage.cleanupExpiredBroadcasts();
    } catch (error) {
      console.error("Error during initial broadcast cleanup:", error);
    }
  }, 5e3);
  const isAdmin = async (req, res, next) => {
    try {
      const userId = parseInt(req.query.userId) || parseInt(req.body.userId) || 1;
      const user = await storage.getUser(userId);
      console.log(`Admin check for userId: ${userId}, user:`, user);
      const hasAdminPhone = user && (user.mobile === "9880105082" || user.mobile === "+919880105082");
      if (userId === 1 || hasAdminPhone || user && user.isAdmin) {
        console.log(`Admin access granted for userId: ${userId}`);
        next();
      } else {
        console.log(`Admin access denied for userId: ${userId}, mobile: ${user?.mobile}`);
        res.status(403).json({ message: "Admin access required" });
      }
    } catch (error) {
      console.error("Error checking admin access:", error);
      res.status(403).json({ message: "Admin access required" });
    }
  };
  app2.get("/api/admin/stats", isAdmin, async (req, res) => {
    try {
      const stats = await storage.getAdminStats();
      res.json(stats);
    } catch (error) {
      console.error("Error fetching admin stats:", error);
      res.status(500).json({ error: "Failed to fetch admin stats" });
    }
  });
  app2.post("/api/admin/users/:userId/block", isAdmin, async (req, res) => {
    try {
      const { userId } = req.params;
      const { reason } = req.body;
      await storage.blockUser(parseInt(userId), reason || "Admin action");
      res.json({ success: true, message: "User blocked successfully" });
    } catch (error) {
      console.error("Error blocking user:", error);
      res.status(500).json({ message: "Failed to block user" });
    }
  });
  app2.post("/api/admin/users/:userId/unblock", isAdmin, async (req, res) => {
    try {
      const { userId } = req.params;
      await storage.unblockUser(parseInt(userId));
      res.json({ success: true, message: "User unblocked successfully" });
    } catch (error) {
      console.error("Error unblocking user:", error);
      res.status(500).json({ message: "Failed to unblock user" });
    }
  });
  app2.delete("/api/admin/users/:userId", isAdmin, async (req, res) => {
    try {
      const { userId } = req.params;
      const userIdInt = parseInt(userId);
      if (userIdInt === 1) {
        return res.status(403).json({ message: "Cannot delete primary admin user" });
      }
      const deleted = await storage.deleteUser(userIdInt);
      if (deleted) {
        res.json({ success: true, message: "User and all associated data deleted successfully" });
      } else {
        res.status(404).json({ message: "User not found" });
      }
    } catch (error) {
      console.error("Error deleting user:", error);
      res.status(500).json({ message: "Failed to delete user" });
    }
  });
  app2.delete("/api/admin/broadcasts/:id", isAdmin, async (req, res) => {
    try {
      const { id } = req.params;
      const deleted = await storage.adminDeleteBroadcast(parseInt(id));
      if (deleted) {
        res.json({ success: true, message: "Post deleted successfully" });
      } else {
        res.status(404).json({ message: "Post not found" });
      }
    } catch (error) {
      console.error("Error deleting post:", error);
      res.status(500).json({ message: "Failed to delete post" });
    }
  });
  app2.post("/api/admin/messages", isAdmin, async (req, res) => {
    try {
      const { message, messageDate } = req.body;
      const adminMessage = await storage.createAdminMessage({
        message,
        messageDate,
        isActive: true
      });
      res.json(adminMessage);
    } catch (error) {
      console.error("Error creating admin message:", error);
      res.status(500).json({ message: "Failed to create admin message" });
    }
  });
  app2.get("/api/admin/messages/today", isAdmin, async (req, res) => {
    try {
      const message = await storage.getTodaysAdminMessage();
      res.json(message);
    } catch (error) {
      console.error("Error fetching today's admin message:", error);
      res.status(500).json({ message: "Failed to fetch admin message" });
    }
  });
  app2.put("/api/admin/messages/:id", isAdmin, async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const { message, messageDate, isActive } = req.body;
      const updated = await storage.updateAdminMessage(id, { message, messageDate, isActive });
      if (updated) {
        res.json(updated);
      } else {
        res.status(404).json({ message: "Admin message not found" });
      }
    } catch (error) {
      console.error("Error updating admin message:", error);
      res.status(500).json({ message: "Failed to update admin message" });
    }
  });
  app2.delete("/api/admin/messages/:id", isAdmin, async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const success = await storage.deleteAdminMessage(id);
      if (success) {
        res.json({ success: true });
      } else {
        res.status(404).json({ message: "Admin message not found" });
      }
    } catch (error) {
      console.error("Error deleting admin message:", error);
      res.status(500).json({ message: "Failed to delete admin message" });
    }
  });
  app2.post("/api/admin/users/:userId/message", isAdmin, async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const { message } = req.body;
      if (!message || message.trim() === "") {
        return res.status(400).json({ message: "Message content is required" });
      }
      const userMessageData = {
        userId,
        message: message.trim(),
        fromAdmin: true,
        isRead: false,
        isDismissed: false
      };
      const createdMessage = await storage.createUserMessage(userMessageData);
      res.status(201).json(createdMessage);
    } catch (error) {
      console.error("Error sending message to user:", error);
      res.status(500).json({ message: "Failed to send message to user" });
    }
  });
  app2.get("/api/user-messages", async (req, res) => {
    try {
      const userId = parseInt(req.query.userId);
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const messages = await storage.getUserMessages(userId);
      res.json(messages);
    } catch (error) {
      console.error("Error fetching user messages:", error);
      res.status(500).json({ message: "Failed to fetch user messages" });
    }
  });
  app2.put("/api/user-messages/:id/dismiss", async (req, res) => {
    try {
      const messageId = parseInt(req.params.id);
      const userId = parseInt(req.query.userId);
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const success = await storage.dismissUserMessage(messageId, userId);
      if (success) {
        res.json({ success: true });
      } else {
        res.status(404).json({ message: "Message not found or already dismissed" });
      }
    } catch (error) {
      console.error("Error dismissing user message:", error);
      res.status(500).json({ message: "Failed to dismiss user message" });
    }
  });
  app2.get("/api/admin/users/recent", isAdmin, async (req, res) => {
    try {
      const limit = parseInt(req.query.limit) || 10;
      const users2 = await storage.getRecentUsers(limit);
      res.json(users2);
    } catch (error) {
      console.error("Error fetching recent users:", error);
      res.status(500).json({ error: "Failed to fetch recent users" });
    }
  });
  app2.get("/api/admin/vehicles/recent", isAdmin, async (req, res) => {
    try {
      const limit = parseInt(req.query.limit) || 10;
      const vehicles2 = await storage.getRecentVehicles(limit);
      res.json(vehicles2);
    } catch (error) {
      console.error("Error fetching recent vehicles:", error);
      res.status(500).json({ error: "Failed to fetch recent vehicles" });
    }
  });
  app2.get("/api/admin/broadcasts/recent", isAdmin, async (req, res) => {
    try {
      const limit = parseInt(req.query.limit) || 10;
      const broadcasts2 = await storage.getRecentBroadcasts(limit);
      res.json(broadcasts2);
    } catch (error) {
      console.error("Error fetching recent broadcasts:", error);
      res.status(500).json({ error: "Failed to fetch recent broadcasts" });
    }
  });
  app2.get("/api/admin/ratings/recent", isAdmin, async (req, res) => {
    try {
      const limit = parseInt(req.query.limit) || 10;
      const ratings2 = await storage.getRecentRatings(limit);
      res.json(ratings2);
    } catch (error) {
      console.error("Error fetching recent ratings:", error);
      res.status(500).json({ error: "Failed to fetch recent ratings" });
    }
  });
  app2.get("/api/admin/export/:dataType", isAdmin, async (req, res) => {
    try {
      const { dataType } = req.params;
      let data;
      let filename;
      let headers = {};
      console.log(`\u{1F50D} ADMIN EXPORT: Exporting ${dataType} data`);
      switch (dataType) {
        case "users":
          data = await storage.getAllUsersData();
          filename = `users-export-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`;
          break;
        case "vehicles":
          data = await storage.getAllVehiclesData();
          filename = `vehicles-export-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`;
          break;
        case "broadcasts":
          data = await storage.getAllBroadcastsData();
          filename = `broadcasts-export-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`;
          break;
        case "ratings":
          data = await storage.getAllRatingsData();
          filename = `ratings-export-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`;
          break;
        default:
          return res.status(400).json({ error: "Invalid data type" });
      }
      console.log(`\u{1F50D} ADMIN EXPORT: Retrieved ${data?.length || 0} ${dataType} records`);
      if (dataType === "vehicles") {
        const totalDocs = data?.reduce((sum, vehicle) => sum + (vehicle.documentCounts?.total || 0), 0) || 0;
        console.log(`\u{1F50D} ADMIN EXPORT: Vehicles include ${totalDocs} total documents`);
        console.log(`\u{1F50D} ADMIN EXPORT: Enhanced data includes insurance, RC, and emission details`);
      }
      const formattedData = {
        exportType: dataType,
        exportDate: (/* @__PURE__ */ new Date()).toISOString(),
        totalRecords: data?.length || 0,
        data: data || []
      };
      res.setHeader("Content-Type", "application/json");
      res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
      res.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
      res.status(200).send(JSON.stringify(formattedData, null, 2));
    } catch (error) {
      console.error("Error exporting data:", error);
      res.status(500).json({ error: "Failed to export data", details: error.message });
    }
  });
  app2.get("/api/account-info", async (req, res) => {
    try {
      const userId = req.query.userId || "1";
      const user = await storage.getUser(parseInt(userId));
      if (!user) {
        return res.status(404).json({ message: "User not found" });
      }
      const subscription = await storage.getCurrentSubscription(userId);
      const paymentHistory2 = await storage.getPaymentHistory(userId);
      let isSubscriptionExpired = false;
      let daysUntilExpiry = 0;
      if (subscription) {
        const now = /* @__PURE__ */ new Date();
        const expiryDate = new Date(subscription.expiryDate);
        isSubscriptionExpired = expiryDate < now;
        daysUntilExpiry = Math.ceil((expiryDate.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
      }
      res.json({
        accountCreatedDate: user.createdAt,
        subscription,
        paymentHistory: paymentHistory2,
        isSubscriptionExpired,
        daysUntilExpiry: Math.max(0, daysUntilExpiry)
      });
    } catch (error) {
      console.error("Error fetching account info:", error);
      res.status(500).json({ message: "Failed to fetch account information" });
    }
  });
  app2.post("/api/subscription/renew", async (req, res) => {
    try {
      const userId = req.body.userId || "1";
      const subscriptionDate = /* @__PURE__ */ new Date();
      const expiryDate = /* @__PURE__ */ new Date();
      expiryDate.setFullYear(expiryDate.getFullYear() + 1);
      const subscription = await storage.createSubscription({
        userId,
        subscriptionType: "premium",
        subscriptionDate: subscriptionDate.toISOString(),
        expiryDate: expiryDate.toISOString(),
        isActive: true,
        amount: 1e4,
        // ₹100 in paise
        currency: "INR"
      });
      const transactionId = `TXN_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      const payment = await storage.createPaymentRecord({
        userId,
        subscriptionId: subscription.id,
        amount: 1e4,
        currency: "INR",
        paymentDate: (/* @__PURE__ */ new Date()).toISOString(),
        transactionId,
        paymentStatus: "success",
        paymentMethod: "upi",
        invoiceGenerated: true,
        invoicePath: `/invoices/invoice_${transactionId}.pdf`
      });
      await generateInvoice(payment, subscription);
      res.json({
        success: true,
        subscription,
        payment,
        message: "Subscription renewed successfully"
      });
    } catch (error) {
      console.error("Error renewing subscription:", error);
      res.status(500).json({ message: "Failed to renew subscription" });
    }
  });
  app2.get("/api/subscription/check-expired-features", async (req, res) => {
    try {
      const userId = req.query.userId || "1";
      const subscription = await storage.getCurrentSubscription(userId);
      let isExpired = true;
      if (subscription) {
        const now = /* @__PURE__ */ new Date();
        const expiryDate = new Date(subscription.expiryDate);
        isExpired = expiryDate < now;
      }
      res.json({
        isExpired,
        restrictedFeatures: isExpired ? ["documents", "add-vehicle", "service-logs", "broadcasts"] : []
      });
    } catch (error) {
      console.error("Error checking subscription status:", error);
      res.status(500).json({ message: "Failed to check subscription status" });
    }
  });
  app2.get("/api/invoice/download/:paymentId", async (req, res) => {
    try {
      const paymentId = req.params.paymentId;
      const payment = await storage.getPaymentById(parseInt(paymentId));
      if (!payment || !payment.invoicePath) {
        return res.status(404).json({ message: "Invoice not found" });
      }
      const invoiceContent = generatePDFContent(payment);
      res.setHeader("Content-Type", "application/pdf");
      res.setHeader("Content-Disposition", `attachment; filename="invoice-${paymentId}.pdf"`);
      res.send(invoiceContent);
    } catch (error) {
      console.error("Error downloading invoice:", error);
      res.status(500).json({ message: "Failed to download invoice" });
    }
  });
  app2.post("/api/push/register-token", async (req, res) => {
    try {
      const { token, userId, platform } = req.body;
      if (!token || !userId) {
        return res.status(400).json({ message: "Token and userId are required" });
      }
      await storage.registerPushToken(userId, token, platform);
      res.json({
        success: true,
        message: "Push token registered successfully"
      });
    } catch (error) {
      console.error("Error registering push token:", error);
      res.status(500).json({ message: "Failed to register push token" });
    }
  });
  app2.post("/api/subscription/send-notifications", async (req, res) => {
    try {
      const activeSubscriptions = await storage.getActiveSubscriptions();
      const now = /* @__PURE__ */ new Date();
      let notificationsSent = 0;
      let pushNotificationsSent = 0;
      for (const subscription of activeSubscriptions) {
        const expiryDate = new Date(subscription.expiryDate);
        const daysUntilExpiry = Math.ceil((expiryDate.getTime() - now.getTime()) / (1e3 * 60 * 60 * 24));
        const weeklyTargets = [30, 23, 16, 9, 2];
        if (weeklyTargets.includes(daysUntilExpiry)) {
          const existingNotification = await storage.getRecentSubscriptionNotification(
            subscription.userId,
            `expiry_warning_${daysUntilExpiry}d`
          );
          if (!existingNotification) {
            await storage.createSubscriptionNotification({
              userId: subscription.userId,
              subscriptionId: subscription.id,
              notificationType: `expiry_warning_${daysUntilExpiry}d`,
              sentDate: now.toISOString(),
              isRead: false,
              title: getNotificationTitle(daysUntilExpiry),
              message: getNotificationMessage(daysUntilExpiry)
            });
            const pushResult = await sendPushNotification(subscription.userId, {
              title: getNotificationTitle(daysUntilExpiry),
              body: getNotificationMessage(daysUntilExpiry),
              data: {
                type: "subscription_expiry",
                daysUntilExpiry,
                subscriptionId: subscription.id
              }
            });
            if (pushResult.success) {
              pushNotificationsSent++;
            }
            notificationsSent++;
            console.log(`Sent subscription reminder: ${daysUntilExpiry} days for user ${subscription.userId}`);
          }
        }
        if (daysUntilExpiry <= 0 && subscription.isActive) {
          await storage.deactivateSubscription(subscription.id);
          await storage.createSubscriptionNotification({
            userId: subscription.userId,
            subscriptionId: subscription.id,
            notificationType: "expired",
            sentDate: now.toISOString(),
            isRead: false,
            title: "MyyMotto Subscription Expired",
            message: "Your premium subscription has expired. Renew now to restore full access!"
          });
          const pushResult = await sendPushNotification(subscription.userId, {
            title: "MyyMotto Subscription Expired",
            body: "Your premium subscription has expired. Renew now to restore full access!",
            data: {
              type: "subscription_expired",
              subscriptionId: subscription.id
            }
          });
          if (pushResult.success) {
            pushNotificationsSent++;
          }
          notificationsSent++;
        }
      }
      res.json({
        success: true,
        notificationsSent,
        pushNotificationsSent,
        message: `Processed ${activeSubscriptions.length} subscriptions, sent ${notificationsSent} notifications (${pushNotificationsSent} push notifications)`
      });
    } catch (error) {
      console.error("Error sending subscription notifications:", error);
      res.status(500).json({ message: "Failed to send notifications" });
    }
  });
  app2.post("/api/subscription/test-notification", async (req, res) => {
    try {
      const { userId, daysUntilExpiry } = req.body;
      const testNotification = {
        title: getNotificationTitle(daysUntilExpiry || 30),
        body: getNotificationMessage(daysUntilExpiry || 30),
        data: {
          type: "subscription_expiry",
          daysUntilExpiry: daysUntilExpiry || 30,
          test: true
        }
      };
      const pushResult = await sendPushNotification(userId || "1", testNotification);
      res.json({
        success: true,
        pushResult,
        notification: testNotification,
        message: "Test notification sent successfully"
      });
    } catch (error) {
      console.error("Error sending test notification:", error);
      res.status(500).json({ message: "Failed to send test notification" });
    }
  });
  app2.get("/api/admin/accounts", isAdmin, async (req, res) => {
    try {
      const accounts = await storage.getAllAccountInformation();
      res.json(accounts);
    } catch (error) {
      console.error("Error fetching account information:", error);
      res.status(500).json({ error: "Failed to fetch account information" });
    }
  });
  app2.get("/api/admin/accounts/:userId", isAdmin, async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const account = await storage.getAccountInformation(userId);
      if (!account) {
        return res.status(404).json({ error: "Account information not found" });
      }
      res.json(account);
    } catch (error) {
      console.error("Error fetching account information:", error);
      res.status(500).json({ error: "Failed to fetch account information" });
    }
  });
  app2.post("/api/admin/accounts", isAdmin, async (req, res) => {
    try {
      const accountData = insertAccountInformationSchema.parse(req.body);
      const account = await storage.createAccountInformation(accountData);
      res.json(account);
    } catch (error) {
      console.error("Error creating account information:", error);
      res.status(500).json({ error: "Failed to create account information" });
    }
  });
  app2.put("/api/admin/accounts/:userId", isAdmin, async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const accountData = insertAccountInformationSchema.partial().parse(req.body);
      const account = await storage.updateAccountInformation(userId, accountData);
      if (!account) {
        return res.status(404).json({ error: "Account information not found" });
      }
      res.json(account);
    } catch (error) {
      console.error("Error updating account information:", error);
      res.status(500).json({ error: "Failed to update account information" });
    }
  });
  app2.get("/api/admin/invoices", isAdmin, async (req, res) => {
    try {
      const invoices2 = await storage.getAllInvoices();
      res.json(invoices2);
    } catch (error) {
      console.error("Error fetching all invoices:", error);
      res.status(500).json({ error: "Failed to fetch invoices" });
    }
  });
  app2.get("/api/admin/invoices/user/:userId", isAdmin, async (req, res) => {
    try {
      const userId = parseInt(req.params.userId);
      const invoices2 = await storage.getInvoices(userId);
      res.json(invoices2);
    } catch (error) {
      console.error("Error fetching user invoices:", error);
      res.status(500).json({ error: "Failed to fetch user invoices" });
    }
  });
  app2.get("/api/admin/invoices/download/:invoiceId", isAdmin, async (req, res) => {
    try {
      const invoiceId = parseInt(req.params.invoiceId);
      const invoice = await storage.getInvoice(invoiceId);
      if (!invoice) {
        return res.status(404).json({ error: "Invoice not found" });
      }
      const invoiceData = {
        invoiceNumber: invoice.invoiceNumber,
        customerName: invoice.customerName,
        customerPhone: invoice.customerPhone,
        customerEmail: invoice.customerEmail,
        amount: invoice.amount,
        totalAmount: invoice.totalAmount,
        description: invoice.description,
        invoiceDate: invoice.invoiceDate,
        dueDate: invoice.dueDate,
        status: invoice.invoiceStatus
      };
      res.setHeader("Content-Type", "application/json");
      res.setHeader("Content-Disposition", `attachment; filename="invoice-${invoice.invoiceNumber}.json"`);
      res.json(invoiceData);
    } catch (error) {
      console.error("Error downloading invoice:", error);
      res.status(500).json({ error: "Failed to download invoice" });
    }
  });
  app2.get("/api/admin/invoices/range", isAdmin, async (req, res) => {
    try {
      const { startDate, endDate } = req.query;
      if (!startDate || !endDate) {
        return res.status(400).json({ error: "Start date and end date are required" });
      }
      const invoices2 = await storage.getInvoicesByDateRange(new Date(startDate), new Date(endDate));
      res.json(invoices2);
    } catch (error) {
      console.error("Error fetching invoices by date range:", error);
      res.status(500).json({ error: "Failed to fetch invoices by date range" });
    }
  });
  app2.get("/api/admin/invoices/overdue", isAdmin, async (req, res) => {
    try {
      const overdueInvoices = await storage.getOverdueInvoices();
      res.json(overdueInvoices);
    } catch (error) {
      console.error("Error fetching overdue invoices:", error);
      res.status(500).json({ error: "Failed to fetch overdue invoices" });
    }
  });
  app2.post("/api/admin/invoices", isAdmin, async (req, res) => {
    try {
      const invoiceData = insertInvoiceSchema.parse(req.body);
      if (!invoiceData.invoiceNumber) {
        invoiceData.invoiceNumber = await storage.generateInvoiceNumber();
      }
      const invoice = await storage.createInvoice(invoiceData);
      res.json(invoice);
    } catch (error) {
      console.error("Error creating invoice:", error);
      res.status(500).json({ error: "Failed to create invoice" });
    }
  });
  app2.put("/api/admin/invoices/:invoiceId/mark-paid", isAdmin, async (req, res) => {
    try {
      const invoiceId = parseInt(req.params.invoiceId);
      const { paymentDate } = req.body;
      const invoice = await storage.markInvoiceAsPaid(invoiceId, new Date(paymentDate || Date.now()));
      if (!invoice) {
        return res.status(404).json({ error: "Invoice not found" });
      }
      res.json(invoice);
    } catch (error) {
      console.error("Error marking invoice as paid:", error);
      res.status(500).json({ error: "Failed to mark invoice as paid" });
    }
  });
  app2.get("/api/todays-message", async (req, res) => {
    try {
      const message = await storage.getTodaysAdminMessage();
      res.json(message);
    } catch (error) {
      console.error("Error fetching today's message:", error);
      res.status(500).json({ message: "Failed to fetch today's message" });
    }
  });
  app2.get("/api/calendar-reminders", async (req, res) => {
    try {
      const userId = req.query.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const reminders = await storage.getCalendarReminders(parsedUserId);
      res.json(reminders);
    } catch (error) {
      console.error("Error fetching calendar reminders:", error);
      res.status(500).json({ message: "Failed to fetch calendar reminders" });
    }
  });
  app2.get("/api/calendar-reminders/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = req.query.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const reminder = await storage.getCalendarReminder(id, parsedUserId);
      if (!reminder) {
        return res.status(404).json({ message: "Calendar reminder not found" });
      }
      res.json(reminder);
    } catch (error) {
      console.error("Error fetching calendar reminder:", error);
      res.status(500).json({ message: "Failed to fetch calendar reminder" });
    }
  });
  app2.post("/api/calendar-reminders", async (req, res) => {
    try {
      const validatedData = insertCalendarReminderSchema.parse(req.body);
      const userId = req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const reminder = await storage.createCalendarReminder({
        ...validatedData,
        userId
      });
      res.status(201).json(reminder);
    } catch (error) {
      console.error("Error creating calendar reminder:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to create calendar reminder" });
      }
    }
  });
  app2.put("/api/calendar-reminders/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = req.body.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const validatedData = insertCalendarReminderSchema.partial().parse(req.body);
      const reminder = await storage.updateCalendarReminder(id, parsedUserId, validatedData);
      if (!reminder) {
        return res.status(404).json({ message: "Calendar reminder not found" });
      }
      res.json(reminder);
    } catch (error) {
      console.error("Error updating calendar reminder:", error);
      if (error instanceof Error) {
        res.status(400).json({ message: error.message });
      } else {
        res.status(500).json({ message: "Failed to update calendar reminder" });
      }
    }
  });
  app2.delete("/api/calendar-reminders/:id", async (req, res) => {
    try {
      const id = parseInt(req.params.id);
      const userId = req.query.userId;
      if (!userId) {
        return res.status(400).json({ message: "User ID is required" });
      }
      const parsedUserId = parseInt(userId);
      if (isNaN(parsedUserId)) {
        return res.status(400).json({ message: "Invalid User ID format" });
      }
      const success = await storage.deleteCalendarReminder(id, parsedUserId);
      if (!success) {
        return res.status(404).json({ message: "Calendar reminder not found" });
      }
      res.json({ message: "Calendar reminder deleted successfully" });
    } catch (error) {
      console.error("Error deleting calendar reminder:", error);
      res.status(500).json({ message: "Failed to delete calendar reminder" });
    }
  });
  app2.post("/api/calendar-reminders/process-notifications", async (req, res) => {
    try {
      const pendingReminders = await storage.getPendingReminders();
      let notificationsSent = 0;
      for (const reminder of pendingReminders) {
        await storage.createNotification({
          userId: reminder.userId,
          vehicleId: null,
          type: "calendar_reminder",
          title: "Calendar Reminder",
          message: `Don't forget: ${reminder.title}`,
          isRead: false,
          actionUrl: "/calendar-reminder"
        });
        await storage.markReminderNotified(reminder.id);
        notificationsSent++;
      }
      res.json({
        success: true,
        message: `Processed ${notificationsSent} calendar reminder notifications`,
        notificationsSent
      });
    } catch (error) {
      console.error("Error processing calendar reminder notifications:", error);
      res.status(500).json({ message: "Failed to process calendar reminder notifications" });
    }
  });
  const httpServer = createServer(app2);
  return httpServer;
}
function getNotificationTitle(daysUntilExpiry) {
  if (daysUntilExpiry <= 0) return "MyyMotto Subscription Expired";
  if (daysUntilExpiry <= 2) return "MyyMotto Critical Alert";
  if (daysUntilExpiry <= 9) return "MyyMotto Final Notice";
  if (daysUntilExpiry <= 16) return "MyyMotto Renewal Urgent";
  if (daysUntilExpiry <= 23) return "MyyMotto Subscription Alert";
  return "MyyMotto Subscription Reminder";
}
function getNotificationMessage(daysUntilExpiry) {
  if (daysUntilExpiry <= 0) return "Your premium subscription has expired. Renew now to restore full access!";
  if (daysUntilExpiry <= 2) return `URGENT: Your subscription expires in ${daysUntilExpiry} days! Renew now to avoid service loss.`;
  if (daysUntilExpiry <= 9) return `Last chance! Your subscription expires in ${daysUntilExpiry} days. Renew immediately!`;
  if (daysUntilExpiry <= 16) return `Your subscription expires in ${daysUntilExpiry} days. Don't lose access to premium features!`;
  if (daysUntilExpiry <= 23) return `Only ${daysUntilExpiry} days left on your premium subscription. Secure your renewal today!`;
  return `Your premium subscription expires in ${daysUntilExpiry} days. Renew now to avoid service interruption!`;
}
async function sendPushNotification(userId, notification) {
  try {
    const pushTokens = await storage.getUserPushTokens(userId);
    if (!pushTokens || pushTokens.length === 0) {
      console.log(`No push tokens found for user ${userId}`);
      return { success: false, error: "No push tokens registered" };
    }
    console.log(`Sending push notification to user ${userId}:`, {
      title: notification.title,
      body: notification.body,
      tokens: pushTokens,
      data: notification.data
    });
    return { success: true };
  } catch (error) {
    console.error("Error sending push notification:", error);
    return { success: false, error: error.message };
  }
}
async function generateInvoice(payment, subscription) {
  console.log(`Generated invoice for payment ${payment.id}`);
}
function generatePDFContent(payment) {
  const content = `
    MYYMOTTO INVOICE
    ================
    
    Invoice ID: ${payment.id}
    Transaction ID: ${payment.transactionId}
    Date: ${new Date(payment.paymentDate).toLocaleDateString("en-IN")}
    Amount: \u20B9${(payment.amount / 100).toLocaleString("en-IN")}
    Status: ${payment.paymentStatus.toUpperCase()}
    
    Description: Annual Premium Subscription
    Validity: 1 Year
    
    Thank you for choosing MyyMotto!
  `;
  return Buffer.from(content, "utf-8");
}

// server/vite.ts
import express from "express";
import fs2 from "fs";
import path3 from "path";
import { createServer as createViteServer, createLogger } from "vite";

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path2 from "path";
import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";
var vite_config_default = defineConfig({
  plugins: [
    react(),
    runtimeErrorOverlay(),
    ...process.env.NODE_ENV !== "production" && process.env.REPL_ID !== void 0 ? [
      await import("@replit/vite-plugin-cartographer").then(
        (m) => m.cartographer()
      )
    ] : []
  ],
  resolve: {
    alias: {
      "@": path2.resolve(import.meta.dirname, "client", "src"),
      "@shared": path2.resolve(import.meta.dirname, "shared"),
      "@assets": path2.resolve(import.meta.dirname, "attached_assets")
    }
  },
  root: path2.resolve(import.meta.dirname, "client"),
  build: {
    outDir: path2.resolve(import.meta.dirname, "dist/public"),
    emptyOutDir: true
  },
  server: {
    fs: {
      strict: true,
      deny: ["**/.*"]
    }
  }
});

// server/vite.ts
import { nanoid } from "nanoid";
var viteLogger = createLogger();
function log(message, source = "express") {
  const formattedTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
    hour: "numeric",
    minute: "2-digit",
    second: "2-digit",
    hour12: true
  });
  console.log(`${formattedTime} [${source}] ${message}`);
}
async function setupVite(app2, server) {
  const serverOptions = {
    middlewareMode: true,
    hmr: { server },
    allowedHosts: true
  };
  const vite = await createViteServer({
    ...vite_config_default,
    configFile: false,
    customLogger: {
      ...viteLogger,
      error: (msg, options) => {
        viteLogger.error(msg, options);
        process.exit(1);
      }
    },
    server: serverOptions,
    appType: "custom"
  });
  app2.use(vite.middlewares);
  app2.use("*", async (req, res, next) => {
    const url = req.originalUrl;
    try {
      const clientTemplate = path3.resolve(
        import.meta.dirname,
        "..",
        "client",
        "index.html"
      );
      let template = await fs2.promises.readFile(clientTemplate, "utf-8");
      template = template.replace(
        `src="/src/main.tsx"`,
        `src="/src/main.tsx?v=${nanoid()}"`
      );
      const page = await vite.transformIndexHtml(url, template);
      res.status(200).set({ "Content-Type": "text/html" }).end(page);
    } catch (e) {
      vite.ssrFixStacktrace(e);
      next(e);
    }
  });
}
function serveStatic(app2) {
  const distPath = path3.resolve(import.meta.dirname, "public");
  if (!fs2.existsSync(distPath)) {
    throw new Error(
      `Could not find the build directory: ${distPath}, make sure to build the client first`
    );
  }
  app2.use(express.static(distPath));
  app2.use("*", (_req, res) => {
    res.sendFile(path3.resolve(distPath, "index.html"));
  });
}

// server/index.ts
var app = express2();
app.use(express2.json());
app.use(express2.urlencoded({ extended: false }));
app.use((req, res, next) => {
  const start = Date.now();
  const path4 = req.path;
  let capturedJsonResponse = void 0;
  const originalResJson = res.json;
  res.json = function(bodyJson, ...args) {
    capturedJsonResponse = bodyJson;
    return originalResJson.apply(res, [bodyJson, ...args]);
  };
  res.on("finish", () => {
    const duration = Date.now() - start;
    if (path4.startsWith("/api")) {
      let logLine = `${req.method} ${path4} ${res.statusCode} in ${duration}ms`;
      if (capturedJsonResponse) {
        logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
      }
      if (logLine.length > 80) {
        logLine = logLine.slice(0, 79) + "\u2026";
      }
      log(logLine);
    }
  });
  next();
});
(async () => {
  const server = await registerRoutes(app);
  app.use((err, _req, res, _next) => {
    const status = err.status || err.statusCode || 500;
    const message = err.message || "Internal Server Error";
    res.status(status).json({ message });
    throw err;
  });
  if (app.get("env") === "development") {
    await setupVite(app, server);
  } else {
    serveStatic(app);
  }
  const port = 5e3;
  server.listen({
    port,
    host: "0.0.0.0",
    reusePort: true
  }, () => {
    log(`serving on port ${port}`);
  });
})();
