import pg from 'pg';

const { Pool } = pg;

if (!process.env.DATABASE_URL) {
  console.error('FATAL: DATABASE_URL is not set. Point it at your Liara PostgreSQL connection string.');
  process.exit(1);
}

// Liara managed Postgres requires TLS when reached over the public endpoint.
// Set DATABASE_SSL=disable only when connecting over the internal private network.
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  ssl: process.env.DATABASE_SSL === 'disable' ? false : { rejectUnauthorized: false },
});
export { pool };

// pg dates come back as JS Date objects; the API historically returned ISO-ish
// strings from SQLite. Coerce timestamp/date OIDs back to the raw string pg sends.
pg.types.setTypeParser(1114, (v) => v); // timestamp without time zone
pg.types.setTypeParser(1184, (v) => v); // timestamptz

/**
 * Translate a better-sqlite3-style statement into a pg query.
 *  - named params (`@name`) when a single plain-object arg is passed
 *  - positional params (`?`) otherwise
 * Returns { text, values } for pool.query.
 */
function buildQuery(sql, args) {
  const isNamed =
    args.length === 1 &&
    args[0] != null &&
    typeof args[0] === 'object' &&
    !Array.isArray(args[0]) &&
    /@\w+/.test(sql);

  if (isNamed) {
    const obj = args[0];
    const values = [];
    const text = sql.replace(/@(\w+)/g, (_, name) => {
      // hasOwn (not `in`) so @__proto__/@constructor can't pull inherited props (prototype pollution)
      values.push(Object.hasOwn(obj, name) ? obj[name] : null);
      return '$' + values.length;
    });
    return { text, values };
  }

  let i = 0;
  const text = sql.replace(/\?/g, () => '$' + ++i);
  return { text, values: args.flat() };
}

/**
 * better-sqlite3-shaped async facade over pg.
 *   db.prepare(sql).get(params)  -> first row | undefined
 *   db.prepare(sql).all(params)  -> rows[]
 *   db.prepare(sql).run(params)  -> { changes, lastInsertRowid, rows }
 * NOTE: every method returns a Promise — callers MUST await.
 * For INSERTs that need the new id, append `RETURNING id` to the SQL;
 * run() exposes it as `lastInsertRowid`.
 */
export const db = {
  prepare(sql) {
    return {
      async get(...args) {
        const { text, values } = buildQuery(sql, args);
        const res = await pool.query(text, values);
        return res.rows[0];
      },
      async all(...args) {
        const { text, values } = buildQuery(sql, args);
        const res = await pool.query(text, values);
        return res.rows;
      },
      async run(...args) {
        const { text, values } = buildQuery(sql, args);
        const res = await pool.query(text, values);
        return {
          changes: res.rowCount,
          lastInsertRowid: res.rows[0]?.id,
          rows: res.rows,
        };
      },
    };
  },
  async exec(sql) {
    await pool.query(sql);
  },
  async query(text, params) {
    return pool.query(text, params);
  },
};

// ── schema (idempotent) — runs once at import via top-level await ──
await pool.query(`
  CREATE TABLE IF NOT EXISTS users (
    id            SERIAL PRIMARY KEY,
    username      TEXT NOT NULL UNIQUE,
    password_hash TEXT NOT NULL,
    role          TEXT NOT NULL DEFAULT 'admin',
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now()
  );

  CREATE TABLE IF NOT EXISTS articles (
    id              TEXT PRIMARY KEY,
    title           TEXT NOT NULL,
    category        TEXT,
    type            TEXT,
    author          TEXT,
    author_role     TEXT,
    author_initial  TEXT,
    publish_date    TEXT,
    pages           INTEGER DEFAULT 0,
    price           INTEGER DEFAULT 0,
    is_free         INTEGER DEFAULT 1,
    summary         TEXT,
    body            TEXT,
    cover_image     TEXT,
    tags            TEXT,
    featured        INTEGER DEFAULT 0,
    status          TEXT DEFAULT 'published',
    key_data        TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_articles_created ON articles(created_at DESC);
  CREATE INDEX IF NOT EXISTS idx_articles_featured ON articles(featured);

  CREATE TABLE IF NOT EXISTS risk_signals (
    id          TEXT PRIMARY KEY,
    quote       TEXT NOT NULL,
    name        TEXT NOT NULL,
    date        TEXT,
    level       TEXT NOT NULL CHECK (level IN ('critical','high','medium','low','opportunity')),
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_risks_sort ON risk_signals(sort_order, created_at);

  CREATE TABLE IF NOT EXISTS events (
    id              TEXT PRIMARY KEY,
    title_fa        TEXT NOT NULL,
    title_en        TEXT,
    date_fa         TEXT,
    date_en         TEXT,
    month_fa        TEXT,
    month_en        TEXT,
    year            TEXT,
    type            TEXT CHECK(type IN ('conference','exhibition','seminar','international')),
    location_fa     TEXT,
    location_en     TEXT,
    city_fa         TEXT,
    city_en         TEXT,
    description_fa  TEXT,
    description_en  TEXT,
    registration_open INTEGER DEFAULT 1,
    sort_order      INTEGER DEFAULT 0,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_events_sort ON events(sort_order, created_at DESC);

  CREATE TABLE IF NOT EXISTS vision_items (
    id          TEXT PRIMARY KEY,
    icon        TEXT,
    title_fa    TEXT NOT NULL,
    title_en    TEXT,
    desc_fa     TEXT,
    desc_en     TEXT,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_vision_sort ON vision_items(sort_order, created_at DESC);

  CREATE TABLE IF NOT EXISTS advisory_board (
    id          TEXT PRIMARY KEY,
    name_fa     TEXT NOT NULL,
    name_en     TEXT,
    role_fa     TEXT,
    role_en     TEXT,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_advisory_sort ON advisory_board(sort_order, created_at DESC);

  CREATE TABLE IF NOT EXISTS team_members (
    id           TEXT PRIMARY KEY,
    name_fa      TEXT NOT NULL,
    name_en      TEXT,
    role_fa      TEXT,
    role_en      TEXT,
    bio_fa       TEXT,
    bio_en       TEXT,
    expertise    TEXT,
    email        TEXT,
    initial      TEXT,
    photo        TEXT,
    report_count INTEGER DEFAULT 0,
    is_expert    INTEGER DEFAULT 0,
    expert_room_fa TEXT,
    expert_room_en TEXT,
    telegram     TEXT,
    linkedin     TEXT,
    twitter      TEXT,
    website      TEXT,
    sort_order   INTEGER DEFAULT 0,
    created_at   TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at   TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_team_sort ON team_members(sort_order, created_at DESC);

  CREATE TABLE IF NOT EXISTS plans (
    id          TEXT PRIMARY KEY,
    name_fa     TEXT NOT NULL,
    name_en     TEXT,
    price       INTEGER DEFAULT 0,
    period_fa   TEXT,
    period_en   TEXT,
    features    TEXT,
    badge_fa    TEXT,
    badge_en    TEXT,
    is_featured INTEGER DEFAULT 0,
    cta_fa      TEXT,
    cta_en      TEXT,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_plans_sort ON plans(sort_order);

  CREATE TABLE IF NOT EXISTS radar_items (
    id            TEXT PRIMARY KEY,
    category      TEXT CHECK(category IN ('market','tech','commodity','geo','energy')),
    img           TEXT,
    author_name   TEXT,
    author_avatar TEXT,
    title_fa      TEXT,
    date_fa       TEXT,
    excerpt_fa    TEXT,
    body_fa       TEXT,
    analysis_fa   TEXT,
    title_en      TEXT,
    date_en       TEXT,
    excerpt_en    TEXT,
    body_en       TEXT,
    analysis_en   TEXT,
    status        TEXT DEFAULT 'published',
    sort_order    INTEGER DEFAULT 0,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_radar_sort ON radar_items(sort_order, created_at DESC);

  CREATE TABLE IF NOT EXISTS radar_pages (
    slug              TEXT PRIMARY KEY,
    source_categories TEXT,
    label_fa          TEXT,
    label_en          TEXT,
    latest_heading_fa TEXT,
    latest_heading_en TEXT,
    featured_title_fa TEXT,
    featured_title_en TEXT,
    featured_date_fa  TEXT,
    featured_date_en  TEXT,
    featured_img      TEXT,
    banner_kicker_fa  TEXT,
    banner_kicker_en  TEXT,
    banner_title_fa   TEXT,
    banner_title_en   TEXT,
    banner_desc_fa    TEXT,
    banner_desc_en    TEXT,
    banner_book_img   TEXT,
    sort_order        INTEGER DEFAULT 0,
    created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at        TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_radar_pages_sort ON radar_pages(sort_order);

  CREATE TABLE IF NOT EXISTS banners (
    id          TEXT PRIMARY KEY,
    overline_fa TEXT,
    overline_en TEXT,
    title_fa    TEXT NOT NULL,
    title_en    TEXT,
    subtitle_fa TEXT,
    subtitle_en TEXT,
    cta_label_fa TEXT,
    cta_label_en TEXT,
    cta_url     TEXT,
    image       TEXT,
    position    TEXT DEFAULT 'hero',
    active      INTEGER DEFAULT 1,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_banners_active ON banners(active, sort_order);

  CREATE TABLE IF NOT EXISTS factory_reports (
    id          TEXT PRIMARY KEY,
    img         TEXT,
    tag_fa      TEXT,
    tag_en      TEXT,
    title_fa    TEXT NOT NULL,
    title_en    TEXT,
    date_fa     TEXT,
    date_en     TEXT,
    excerpt_fa  TEXT,
    excerpt_en  TEXT,
    file_url    TEXT,
    scroll_dir  TEXT CHECK(scroll_dir IN ('left','right','up')) DEFAULT 'up',
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_factory_reports_sort ON factory_reports(sort_order, created_at DESC);

  CREATE TABLE IF NOT EXISTS integrations (
    id          TEXT PRIMARY KEY,
    name        TEXT NOT NULL,
    icon        TEXT,
    logo        TEXT,
    accent      TEXT,
    grid_col    INTEGER DEFAULT 1,
    grid_row    INTEGER DEFAULT 1,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_integrations_sort ON integrations(sort_order, created_at DESC);

  CREATE TABLE IF NOT EXISTS institute_stats (
    id          TEXT PRIMARY KEY,
    label_fa    TEXT NOT NULL,
    label_en    TEXT,
    value       INTEGER DEFAULT 0,
    suffix_fa   TEXT,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_institute_stats_sort ON institute_stats(sort_order);

  CREATE TABLE IF NOT EXISTS market_prices (
    id             TEXT PRIMARY KEY,
    name           TEXT NOT NULL,
    value          REAL DEFAULT 0,
    unit           TEXT,
    change         REAL DEFAULT 0,
    change_percent REAL DEFAULT 0,
    trend          TEXT CHECK(trend IN ('up','down','flat')) DEFAULT 'flat',
    sort_order     INTEGER DEFAULT 0,
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at     TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_market_prices_sort ON market_prices(sort_order);

  CREATE TABLE IF NOT EXISTS market_chart_points (
    id          TEXT PRIMARY KEY,
    series      TEXT NOT NULL CHECK(series IN ('history','export','comparison')),
    label       TEXT NOT NULL,
    value       REAL DEFAULT 0,
    meta        TEXT,
    sort_order  INTEGER DEFAULT 0,
    created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_chart_points_series ON market_chart_points(series, sort_order);

  CREATE TABLE IF NOT EXISTS prices (
    symbol        TEXT PRIMARY KEY,
    name          TEXT NOT NULL,
    value         REAL,
    unit          TEXT,
    change_value  REAL,
    change_pct    REAL,
    source_url    TEXT,
    fetched_at    TIMESTAMPTZ NOT NULL DEFAULT now()
  );

  CREATE TABLE IF NOT EXISTS members (
    id            SERIAL PRIMARY KEY,
    name          TEXT NOT NULL,
    email         TEXT NOT NULL UNIQUE,
    password_hash TEXT NOT NULL,
    phone         TEXT,
    avatar        TEXT,
    is_active     INTEGER NOT NULL DEFAULT 1,
    created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at    TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_members_email ON members(email);

  CREATE TABLE IF NOT EXISTS purchases (
    id            SERIAL PRIMARY KEY,
    member_id     INTEGER NOT NULL REFERENCES members(id) ON DELETE CASCADE,
    article_id    TEXT NOT NULL REFERENCES articles(id) ON DELETE CASCADE,
    amount        INTEGER NOT NULL DEFAULT 0,
    purchased_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
    UNIQUE(member_id, article_id)
  );
  CREATE INDEX IF NOT EXISTS idx_purchases_member ON purchases(member_id, purchased_at DESC);
`);

// idempotent column adds (Postgres supports IF NOT EXISTS on ADD COLUMN)
for (const sql of [
  "ALTER TABLE articles ADD COLUMN IF NOT EXISTS status TEXT DEFAULT 'published'",
  'ALTER TABLE articles ADD COLUMN IF NOT EXISTS key_data TEXT',
  'ALTER TABLE radar_items ADD COLUMN IF NOT EXISTS body_fa TEXT',
  'ALTER TABLE radar_items ADD COLUMN IF NOT EXISTS body_en TEXT',
  'ALTER TABLE radar_items ADD COLUMN IF NOT EXISTS analysis_fa TEXT',
  'ALTER TABLE radar_items ADD COLUMN IF NOT EXISTS analysis_en TEXT',
  'ALTER TABLE banners ADD COLUMN IF NOT EXISTS overline_fa TEXT',
  'ALTER TABLE banners ADD COLUMN IF NOT EXISTS overline_en TEXT',
  "ALTER TABLE users ADD COLUMN IF NOT EXISTS role TEXT NOT NULL DEFAULT 'admin'",
  'ALTER TABLE articles ADD COLUMN IF NOT EXISTS blocks TEXT',
  'ALTER TABLE articles ADD COLUMN IF NOT EXISTS sources TEXT',
  'ALTER TABLE radar_items ADD COLUMN IF NOT EXISTS tags TEXT',
  'ALTER TABLE radar_items ADD COLUMN IF NOT EXISTS source TEXT',
  "ALTER TABLE radar_items ADD COLUMN IF NOT EXISTS status TEXT DEFAULT 'published'",
  'ALTER TABLE members ADD COLUMN IF NOT EXISTS last_login TIMESTAMPTZ',
  'ALTER TABLE members ADD COLUMN IF NOT EXISTS token_version INTEGER NOT NULL DEFAULT 0',
  'ALTER TABLE users ADD COLUMN IF NOT EXISTS token_version INTEGER NOT NULL DEFAULT 0',
  'ALTER TABLE factory_reports ADD COLUMN IF NOT EXISTS file_url TEXT',
]) {
  await pool.query(sql);
}

// per-member activity log (page views + element clicks) — logged-in users only
await pool.query(`
  CREATE TABLE IF NOT EXISTS member_activity (
    id         SERIAL PRIMARY KEY,
    member_id  INTEGER NOT NULL REFERENCES members(id) ON DELETE CASCADE,
    kind       TEXT NOT NULL,          -- 'view' | 'click'
    path       TEXT,
    label      TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
  );
  CREATE INDEX IF NOT EXISTS idx_activity_member ON member_activity(member_id, created_at DESC);
`);

// newsletter subscribers (footer signup) — email is unique
await pool.query(`
  CREATE TABLE IF NOT EXISTS subscribers (
    id         SERIAL PRIMARY KEY,
    email      TEXT NOT NULL UNIQUE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
  );
`);

// promote the founding admin (lowest id) to owner if none exists yet
const { rows: ownerRows } = await pool.query("SELECT 1 FROM users WHERE role = 'owner' LIMIT 1");
if (ownerRows.length === 0) {
  await pool.query("UPDATE users SET role = 'owner' WHERE id = (SELECT id FROM users ORDER BY id LIMIT 1)");
}

export function rowToPrice(row) {
  if (!row) return null;
  return {
    symbol: row.symbol,
    name: row.name,
    value: row.value,
    unit: row.unit,
    changeValue: row.change_value,
    changePct: row.change_pct,
    sourceUrl: row.source_url,
    fetchedAt: row.fetched_at,
  };
}

export async function upsertPrice(p) {
  await pool.query(
    `INSERT INTO prices (symbol, name, value, unit, change_value, change_pct, source_url, fetched_at)
     VALUES ($1, $2, $3, $4, $5, $6, $7, now())
     ON CONFLICT(symbol) DO UPDATE SET
       name = EXCLUDED.name,
       value = EXCLUDED.value,
       unit = EXCLUDED.unit,
       change_value = EXCLUDED.change_value,
       change_pct = EXCLUDED.change_pct,
       source_url = EXCLUDED.source_url,
       fetched_at = EXCLUDED.fetched_at`,
    [
      p.symbol,
      p.name,
      p.value ?? null,
      p.unit ?? null,
      p.changeValue ?? null,
      p.changePct ?? null,
      p.sourceUrl ?? null,
    ],
  );
}

export function rowToEvent(row) {
  if (!row) return null;
  return {
    id: row.id,
    titleFa: row.title_fa,
    titleEn: row.title_en,
    dateFa: row.date_fa,
    dateEn: row.date_en,
    monthFa: row.month_fa,
    monthEn: row.month_en,
    year: row.year,
    type: row.type,
    locationFa: row.location_fa,
    locationEn: row.location_en,
    cityFa: row.city_fa,
    cityEn: row.city_en,
    descriptionFa: row.description_fa,
    descriptionEn: row.description_en,
    registrationOpen: !!row.registration_open,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToTeamMember(row) {
  if (!row) return null;
  return {
    id: row.id,
    nameFa: row.name_fa,
    nameEn: row.name_en,
    roleFa: row.role_fa,
    roleEn: row.role_en,
    bioFa: row.bio_fa,
    bioEn: row.bio_en,
    expertise: row.expertise ? JSON.parse(row.expertise) : [],
    email: row.email,
    initial: row.initial,
    photo: row.photo,
    reportCount: row.report_count,
    isExpert: !!row.is_expert,
    expertRoomFa: row.expert_room_fa,
    expertRoomEn: row.expert_room_en,
    telegram: row.telegram,
    linkedin: row.linkedin,
    twitter: row.twitter,
    website: row.website,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToPlan(row) {
  if (!row) return null;
  return {
    id: row.id,
    nameFa: row.name_fa,
    nameEn: row.name_en,
    price: row.price,
    periodFa: row.period_fa,
    periodEn: row.period_en,
    features: row.features ? JSON.parse(row.features) : [],
    badgeFa: row.badge_fa,
    badgeEn: row.badge_en,
    isFeatured: !!row.is_featured,
    ctaFa: row.cta_fa,
    ctaEn: row.cta_en,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToRiskSignal(row) {
  if (!row) return null;
  return {
    id: row.id,
    quote: row.quote,
    name: row.name,
    date: row.date,
    level: row.level,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToFactoryReport(row) {
  if (!row) return null;
  return {
    id: row.id,
    img: row.img,
    tagFa: row.tag_fa,
    tagEn: row.tag_en,
    titleFa: row.title_fa,
    titleEn: row.title_en,
    dateFa: row.date_fa,
    dateEn: row.date_en,
    excerptFa: row.excerpt_fa,
    excerptEn: row.excerpt_en,
    fileUrl: row.file_url,
    scrollDir: row.scroll_dir,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToIntegration(row) {
  if (!row) return null;
  return {
    id: row.id,
    name: row.name,
    icon: row.icon,
    logo: row.logo,
    accent: row.accent,
    gridCol: row.grid_col,
    gridRow: row.grid_row,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToInstituteStat(row) {
  if (!row) return null;
  return {
    id: row.id,
    labelFa: row.label_fa,
    labelEn: row.label_en,
    value: row.value,
    suffixFa: row.suffix_fa,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToMarketPrice(row) {
  if (!row) return null;
  return {
    id: row.id,
    name: row.name,
    value: row.value,
    unit: row.unit,
    change: row.change,
    changePercent: row.change_percent,
    trend: row.trend,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToMarketChartPoint(row) {
  if (!row) return null;
  return {
    id: row.id,
    series: row.series,
    label: row.label,
    value: row.value,
    meta: row.meta,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToVisionItem(row) {
  if (!row) return null;
  return {
    id: row.id,
    icon: row.icon,
    titleFa: row.title_fa,
    titleEn: row.title_en,
    descFa: row.desc_fa,
    descEn: row.desc_en,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToAdvisoryMember(row) {
  if (!row) return null;
  return {
    id: row.id,
    nameFa: row.name_fa,
    nameEn: row.name_en,
    roleFa: row.role_fa,
    roleEn: row.role_en,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToRadarItem(row) {
  if (!row) return null;
  return {
    id: row.id,
    category: row.category,
    img: row.img,
    authorName: row.author_name,
    authorAvatar: row.author_avatar,
    titleFa: row.title_fa,
    dateFa: row.date_fa,
    excerptFa: row.excerpt_fa,
    bodyFa: row.body_fa || null,
    analysisFa: row.analysis_fa || null,
    tags: row.tags ? JSON.parse(row.tags) : [],
    source: row.source || null,
    titleEn: row.title_en,
    dateEn: row.date_en,
    excerptEn: row.excerpt_en,
    bodyEn: row.body_en || null,
    analysisEn: row.analysis_en || null,
    status: row.status || 'published',
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToRadarPage(row) {
  if (!row) return null;
  return {
    slug: row.slug,
    sourceCategories: row.source_categories ? JSON.parse(row.source_categories) : [],
    labelFa: row.label_fa,
    labelEn: row.label_en,
    latestHeadingFa: row.latest_heading_fa,
    latestHeadingEn: row.latest_heading_en,
    featuredTitleFa: row.featured_title_fa,
    featuredTitleEn: row.featured_title_en,
    featuredDateFa: row.featured_date_fa,
    featuredDateEn: row.featured_date_en,
    featuredImg: row.featured_img,
    bannerKickerFa: row.banner_kicker_fa,
    bannerKickerEn: row.banner_kicker_en,
    bannerTitleFa: row.banner_title_fa,
    bannerTitleEn: row.banner_title_en,
    bannerDescFa: row.banner_desc_fa,
    bannerDescEn: row.banner_desc_en,
    bannerBookImg: row.banner_book_img,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToBanner(row) {
  if (!row) return null;
  return {
    id: row.id,
    overlineFa: row.overline_fa,
    overlineEn: row.overline_en,
    titleFa: row.title_fa,
    titleEn: row.title_en,
    subtitleFa: row.subtitle_fa,
    subtitleEn: row.subtitle_en,
    ctaLabelFa: row.cta_label_fa,
    ctaLabelEn: row.cta_label_en,
    ctaUrl: row.cta_url,
    image: row.image,
    position: row.position,
    active: !!row.active,
    sortOrder: row.sort_order,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

export function rowToArticle(row) {
  if (!row) return null;
  return {
    id: row.id,
    title: row.title,
    category: row.category,
    type: row.type,
    author: row.author,
    authorRole: row.author_role,
    authorInitial: row.author_initial,
    publishDate: row.publish_date,
    pages: row.pages,
    price: row.price,
    isFree: !!row.is_free,
    summary: row.summary,
    body: row.body,
    coverImage: row.cover_image,
    blocks: row.blocks ? JSON.parse(row.blocks) : [],
    sources: row.sources ? JSON.parse(row.sources) : [],
    tags: row.tags ? JSON.parse(row.tags) : [],
    featured: !!row.featured,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}
