const { Client } = require('pg');
const fs = require('fs');
const path = require('path');
require('dotenv').config({ path: '/root/miaojingAI/.env.local' });

const root = '/root/miaojingAI';
const client = new Client({ connectionString: process.env.LOCAL_DB_URL });

function storageKeyFromUrl(url) {
  if (!url || typeof url !== 'string') return null;
  const marker = '/api/local-storage/';
  const index = url.indexOf(marker);
  if (index < 0) return null;
  try {
    return decodeURIComponent(url.slice(index + marker.length).split('?')[0]);
  } catch {
    return null;
  }
}

function countFiles(dir) {
  if (!fs.existsSync(dir)) return 0;
  let count = 0;
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    const full = path.join(dir, entry.name);
    count += entry.isDirectory() ? countFiles(full) : 1;
  }
  return count;
}

async function q(name, sql) {
  const result = await client.query(sql);
  return { name, rows: result.rows };
}

(async () => {
  await client.connect();
  const checks = {};

  checks.tableCounts = await q('table_counts', `
    select 'auth.users' as table_name, count(*)::int as count from auth.users
    union all select 'profiles', count(*)::int from profiles
    union all select 'user_api_keys', count(*)::int from user_api_keys
    union all select 'works', count(*)::int from works
    union all select 'generation_jobs', count(*)::int from generation_jobs
    union all select 'credit_transactions', count(*)::int from credit_transactions
    union all select 'orders', count(*)::int from orders
    union all select 'api_providers', count(*)::int from api_providers
    union all select 'model_recommendations', count(*)::int from model_recommendations
  `);

  checks.profiles = await q('profiles', `
    select id, email, nickname, role, membership_tier, credits_balance, is_active,
           avatar_url is not null as has_avatar, created_at, updated_at
    from profiles
    order by created_at desc
  `);

  checks.profileAuthIntegrity = await q('profile_auth_integrity', `
    select 'profile_without_auth' as issue, count(*)::int as count
    from profiles p left join auth.users u on u.id = p.id where u.id is null
    union all
    select 'auth_without_profile', count(*)::int
    from auth.users u left join profiles p on p.id = u.id where p.id is null
  `);

  checks.admin = await q('admin', `
    select p.id, p.email, p.nickname, p.role, p.membership_tier, p.is_active,
           u.password_hash is not null as has_password_hash
    from profiles p
    left join auth.users u on u.id = p.id
    where p.role = 'admin' or p.email = 'admin@example.com'
    order by p.created_at
  `);

  checks.apiKeys = await q('api_keys', `
    select id, user_id, provider, supplier_name, type, model_name, note,
           api_key_preview, length(api_key_encrypted) as encrypted_len,
           api_key_encrypted like 'mjenc:v1:%' as has_enc_prefix,
           api_key_encrypted = api_key_preview as encrypted_equals_preview,
           is_active, created_at
    from user_api_keys
    order by created_at desc
  `);

  checks.apiKeyIntegrity = await q('api_key_integrity', `
    select count(*)::int as orphan_api_keys
    from user_api_keys k left join profiles p on p.id = k.user_id
    where p.id is null
  `);

  checks.worksDistribution = await q('works_distribution', `
    select type, status, is_public, count(*)::int as count,
           count(*) filter (where result_url like '/api/local-storage/%')::int as local_urls,
           count(*) filter (where result_url like 'data:%')::int as data_urls,
           count(*) filter (where result_url like '[%')::int as placeholder_urls
    from works
    group by type, status, is_public
    order by type, status, is_public
  `);

  checks.worksByUser = await q('works_by_user', `
    select p.email, p.nickname, w.user_id, count(*)::int as works
    from works w
    left join profiles p on p.id = w.user_id
    group by p.email, p.nickname, w.user_id
    order by works desc nulls last
  `);

  checks.workIntegrity = await q('work_integrity', `
    select 'works_missing_user' as issue, count(*)::int as count
    from works w left join profiles p on p.id = w.user_id
    where p.id is null
    union all
    select 'works_null_user', count(*)::int from works where user_id is null
    union all
    select 'works_bad_url', count(*)::int
    from works
    where result_url is null or result_url = '' or result_url like 'data:%'
    union all
    select 'public_not_gallery_url', count(*)::int
    from works
    where is_public = true and result_url not like '/api/local-storage/gallery/%'
  `);

  checks.reversePromptWorks = await q('reverse_prompt_works', `
    select id, user_id, type,
           left(result_url, 100) as result_url,
           left(coalesce(params->>'referenceImage', ''), 100) as reference_image,
           prompt <> '' as has_prompt,
           negative_prompt is not null as has_negative_prompt,
           created_at
    from works
    where type = 'reverse-prompt'
    order by created_at desc
    limit 20
  `);

  checks.galleryWorks = await q('gallery_works', `
    select id, user_id, type, is_public, left(result_url, 100) as result_url, created_at
    from works
    where is_public = true
    order by created_at desc
  `);

  checks.generationJobs = await q('generation_jobs', `
    select status, count(*)::int as count,
           count(*) filter (where user_id is null)::int as null_user_count
    from generation_jobs
    group by status
    order by status
  `);

  checks.generationJobsByUser = await q('generation_jobs_by_user', `
    select p.email, p.nickname, j.user_id, count(*)::int as jobs
    from generation_jobs j
    left join profiles p on p.id = j.user_id
    group by p.email, p.nickname, j.user_id
    order by jobs desc nulls last
  `);

  const works = (await client.query(`
    select id, result_url, thumbnail_url, params
    from works
    order by created_at desc
  `)).rows;

  const missingFiles = [];
  let referencedLocalUrls = 0;
  for (const work of works) {
    const urls = [
      ['result_url', work.result_url],
      ['thumbnail_url', work.thumbnail_url],
      ['referenceImage', work.params && work.params.referenceImage],
    ];
    if (work.params && Array.isArray(work.params.referenceImages)) {
      work.params.referenceImages.forEach((url, index) => urls.push([`referenceImages[${index}]`, url]));
    }
    for (const [field, url] of urls) {
      const key = storageKeyFromUrl(url);
      if (!key) continue;
      referencedLocalUrls++;
      const full = path.join(root, 'local-storage', key);
      if (!fs.existsSync(full)) missingFiles.push({ workId: work.id, field, key });
    }
  }

  checks.storage = {
    localStorageExists: fs.existsSync(path.join(root, 'local-storage')),
    backupsExists: fs.existsSync(path.join(root, 'backups')),
    localFiles: countFiles(path.join(root, 'local-storage')),
    generatedFiles: countFiles(path.join(root, 'local-storage', 'generated')),
    galleryFiles: countFiles(path.join(root, 'local-storage', 'gallery')),
    reversePromptFiles: countFiles(path.join(root, 'local-storage', 'reverse-prompt')),
    referencedLocalUrls,
    missingFiles,
  };

  console.log(JSON.stringify(checks, null, 2));
  await client.end();
})().catch((error) => {
  console.error(error);
  process.exit(1);
});
