import { PrismaClient } from '../src/generated/prisma/index';
import { existsSync, readFileSync } from 'fs';
import { join } from 'path';
import { hashPassword } from '../src/auth/password.service';
import {
  normalizeHolderInch,
  parseHolderImport,
  parseInchValue,
} from '../src/holder-master/holder-master-import';
import { parsePlateImport } from '../src/plate-master/plate-master-import';
import {
  parseWheelImport,
  parseWheelInchValue,
} from '../src/wheel-master/wheel-master-import';

loadSeedEnv();

const prisma = new PrismaClient();
const shouldSeedReferenceTsvs = process.env.SEED_REFERENCE_TSVS === 'true';
type SeedUserRole = 'ADMIN' | 'MANAGER' | 'STAFF' | 'EMPLOYEE' | 'USER';
const ACTIVE_USER_STATUS = 'ACTIVE';

const users = [
  {
    email: 'admin@erpstarline.com',
    name: 'Starline Admin',
    role: 'ADMIN' as SeedUserRole,
    password: 'Admin@123',
  },
  {
    email: 'manager@erpstarline.com',
    name: 'Starline Manager',
    role: 'MANAGER' as SeedUserRole,
    password: 'Manager@123',
  },
  {
    email: 'employee@erpstarline.com',
    name: 'Starline Employee',
    role: 'EMPLOYEE' as SeedUserRole,
    password: 'Employee@123',
  },
  {
    email: 'user@erpstarline.com',
    name: 'Starline User',
    role: 'USER' as SeedUserRole,
    password: 'User@123',
  },
];

async function main() {
  await prisma.company.upsert({
    where: { name: 'ERP Starline' },
    update: {},
    create: { name: 'ERP Starline' },
  });

  for (const user of users) {
    await prisma.user.upsert({
      where: { email: user.email },
      update: {
        name: user.name,
        role: user.role,
        status: ACTIVE_USER_STATUS,
        passwordHash: await hashPassword(user.password),
      },
      create: {
        email: user.email,
        name: user.name,
        role: user.role,
        status: ACTIVE_USER_STATUS,
        passwordHash: await hashPassword(user.password),
      },
    });
  }

  if (shouldSeedReferenceTsvs) {
    const holderMasterPath = join(__dirname, 'holder-master.tsv');
    const plateMasterPath = join(__dirname, 'plate-master.tsv');
    const wheelMasterPath = join(__dirname, 'wheel-master.tsv');
    const rawMaterialPath = join(__dirname, 'raw-material.tsv');
    const existingHolderCount = await prisma.holderMaster.count();
    const existingPlateCount = await prisma.plateMaster.count();
    const existingWheelCount = await prisma.wheelMaster.count();
    const existingRawMaterialCount = await prisma.rawMaterial.count();

    if (existingHolderCount === 0 && existsSync(holderMasterPath)) {
      const parsed = parseHolderImport(readFileSync(holderMasterPath, 'utf8'));

      for (const holder of parsed.rows) {
        await prisma.holderMaster.upsert({
          where: { holderCode: holder.holderCode },
          update: holder,
          create: holder,
        });
      }

      if (parsed.errors.length) {
        console.warn('Holder seed warnings:', parsed.errors.slice(0, 10));
      }
    }

    if (existingPlateCount === 0 && existsSync(plateMasterPath)) {
      const parsed = parsePlateImport(readFileSync(plateMasterPath, 'utf8'));

      for (const plate of parsed.rows) {
        await prisma.plateMaster.upsert({
          where: { plateCode: plate.plateCode },
          update: plate,
          create: plate,
        });
      }

      if (parsed.errors.length) {
        console.warn('Plate seed warnings:', parsed.errors.slice(0, 10));
      }
    }

    if (existingWheelCount === 0 && existsSync(wheelMasterPath)) {
      const parsed = parseWheelImport(readFileSync(wheelMasterPath, 'utf8'));

      for (const wheel of parsed.rows) {
        await prisma.wheelMaster.upsert({
          where: { wheelCode: wheel.wheelCode },
          update: wheel,
          create: wheel,
        });
      }

      if (parsed.errors.length) {
        console.warn('Wheel seed warnings:', parsed.errors.slice(0, 10));
      }
    }

    if (existingRawMaterialCount === 0 && existsSync(rawMaterialPath)) {
      const rows = parseRawMaterialSeed(readFileSync(rawMaterialPath, 'utf8'));
      const uom = await prisma.unitOfMeasure.upsert({
        where: { uomCode: 'UNIT' },
        update: {},
        create: {
          uomName: 'Unit',
          uomCode: 'UNIT',
          measureType: 'Quantity',
          conversionFactor: 1,
          status: 'Active',
        },
      });
      const categoryByName = new Map<string, string>();

      for (const row of rows) {
        let category = row.categoryCode
          ? await prisma.rawMaterialCategory.findUnique({
              where: { categoryCode: row.categoryCode },
            })
          : null;

        if (!category) {
          category = await prisma.rawMaterialCategory.upsert({
            where: { categoryName: row.categoryName },
            update: {
              categoryCode: row.categoryCode || undefined,
              description: row.categoryCode ? `Source category code ${row.categoryCode}` : undefined,
            },
            create: {
              categoryCode: row.categoryCode || undefined,
              categoryName: row.categoryName,
              description: row.categoryCode ? `Source category code ${row.categoryCode}` : undefined,
              status: 'Active',
            },
          });
        }

        categoryByName.set(row.categoryName, category.id);
      }

      for (const row of rows) {
        const material = await prisma.rawMaterial.upsert({
          where: { materialCode: row.materialCode },
          update: {},
          create: {
            materialCode: row.materialCode,
            materialName: row.materialName,
            categoryId: categoryByName.get(row.categoryName),
            uomId: uom.id,
            costPerUnit: row.costPerUnit,
            currency: 'INR',
            stockType: 'Central Store',
            status: 'Active',
          },
        });

        if (row.costPerUnit > 0) {
          await prisma.rawMaterialPriceHistory.create({
            data: {
              rawMaterialId: material.id,
              oldPrice: 0,
              newPrice: row.costPerUnit,
              currency: 'INR',
              updatedBy: 'Seed',
              remarks: 'Initial price from raw material source file',
            },
          });
        }
      }
    }
  }

  await prisma.rawMaterialSettings.upsert({
    where: { key: 'raw_material' },
    update: {},
    create: {
      key: 'raw_material',
      smtpHost: 'mail.castorwheel.co',
      smtpPort: 465,
      smtpSecure: true,
      smtpUsername: 'castorwheel@castorwheel.co',
      smtpPassword: 'nadathara#123#wheel',
      defaultSenderEmail: 'castorwheel@castorwheel.co',
      defaultSenderName: 'Castorwheel Support',
      defaultCc: 'accounts@starlinecastors.com',
      billHeader: 'STARLINE CASTORS - PURCHASE ORDER',
      billFooter: 'Thank you for supporting Starline purchase operations.',
      processCodeAutoGenerate: true,
      processCodePrefix: 'PRC',
    },
  });

  const holders = await prisma.holderMaster.findMany({
    select: {
      id: true,
      holderInch: true,
      holderInchValue: true,
    },
  });

  for (const holder of holders) {
    const parsedValue = parseInchValue(holder.holderInch);

    if (!parsedValue) {
      continue;
    }

    const normalizedInch = normalizeHolderInch(holder.holderInch);

    if (
      holder.holderInchValue !== parsedValue ||
      holder.holderInch !== normalizedInch
    ) {
      await prisma.holderMaster.update({
        where: { id: holder.id },
        data: {
          holderInchValue: parsedValue,
          holderInch: normalizedInch,
        },
      });
    }
  }

  const wheels = await prisma.wheelMaster.findMany({
    select: {
      id: true,
      wheelSize: true,
      wheelInchValue: true,
    },
  });

  for (const wheel of wheels) {
    const parsedValue = parseWheelInchValue(wheel.wheelSize);

    if (!parsedValue || wheel.wheelInchValue === parsedValue) {
      continue;
    }

    await prisma.wheelMaster.update({
      where: { id: wheel.id },
      data: { wheelInchValue: parsedValue },
    });
  }
}

main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (error) => {
    console.error(error);
    await prisma.$disconnect();
    process.exit(1);
  });

function parseRawMaterialSeed(text: string) {
  const rows = parseDelimitedRows(text.replace(/^\uFEFF/, '')).filter((row) =>
    row.some((cell) => cleanSeedText(cell)),
  );

  if (rows.length < 2) {
    return [];
  }

  const headers = rows[0].map((header) => cleanSeedText(header).toLowerCase());
  const nameIndex = headers.indexOf('item name');
  const codeIndex = headers.indexOf('item code');
  const priceIndex = headers.indexOf('unit price rupees per uom');
  const categoryIndex = headers.findIndex((header) => header === 'catergory' || header === 'category');
  const categoryCodeIndex = headers.findIndex((header) => header === 'catergory code' || header === 'category code');

  return rows
    .slice(1)
    .map((row) => ({
      materialName: cleanSeedText(row[nameIndex]),
      materialCode: cleanSeedText(row[codeIndex]).toUpperCase(),
      costPerUnit: Number.parseFloat(cleanSeedText(row[priceIndex])) || 0,
      categoryName: cleanSeedText(row[categoryIndex]).toUpperCase() || 'RAW MATERIAL',
      categoryCode: cleanSeedText(row[categoryCodeIndex]).toUpperCase(),
    }))
    .filter((row) => row.materialName && row.materialCode);
}

function parseDelimitedRows(text: string) {
  const firstLine = text.split(/\r?\n/, 1)[0] || '';
  const delimiter = firstLine.includes('\t') ? '\t' : ',';
  const rows: string[][] = [];
  let row: string[] = [];
  let cell = '';
  let inQuotes = false;

  for (let index = 0; index < text.length; index += 1) {
    const char = text[index];
    const nextChar = text[index + 1];

    if (char === '"') {
      if (inQuotes && nextChar === '"') {
        cell += '"';
        index += 1;
      } else {
        inQuotes = !inQuotes;
      }
      continue;
    }

    if (!inQuotes && char === delimiter) {
      row.push(cell);
      cell = '';
      continue;
    }

    if (!inQuotes && (char === '\n' || char === '\r')) {
      if (char === '\r' && nextChar === '\n') {
        index += 1;
      }
      row.push(cell);
      rows.push(row);
      row = [];
      cell = '';
      continue;
    }

    cell += char;
  }

  row.push(cell);
  rows.push(row);
  return rows;
}

function cleanSeedText(value: unknown) {
  return String(value ?? '').trim();
}

function loadSeedEnv() {
  const envFiles = [join(process.cwd(), '.env'), join(process.cwd(), 'prisma', '.env')];

  for (const envFile of envFiles) {
    if (!existsSync(envFile)) {
      continue;
    }

    const content = readFileSync(envFile, 'utf8');

    for (const line of content.split(/\r?\n/)) {
      const trimmed = line.trim();

      if (!trimmed || trimmed.startsWith('#')) {
        continue;
      }

      const separatorIndex = trimmed.indexOf('=');

      if (separatorIndex === -1) {
        continue;
      }

      const key = trimmed.slice(0, separatorIndex).trim();

      if (!key || process.env[key]) {
        continue;
      }

      let value = trimmed.slice(separatorIndex + 1).trim();

      if (
        (value.startsWith('"') && value.endsWith('"')) ||
        (value.startsWith("'") && value.endsWith("'"))
      ) {
        value = value.slice(1, -1);
      }

      process.env[key] = value;
    }
  }
}
