import { hasTable } from './db.js';

async function hasColumn(pool, tableName, columnName) {
  const [rows] = await pool.query(
    'SELECT COUNT(*) AS c FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?',
    [tableName, columnName]
  );
  return (rows?.[0]?.c ?? 0) > 0;
}

export async function initMySqlSupport(pool) {
  return {
    hasMatchTicks: await hasTable(pool, 'match_ticks'),
    matchesHasDiscipline: await hasColumn(pool, 'matches', 'Discipline'),
    matchesHasParticipants: await hasColumn(pool, 'matches', 'Participants'),
    matchesHasRate: await hasColumn(pool, 'matches', 'Rate'),
    matchesHasScore: await hasColumn(pool, 'matches', 'Score'),
    matchesHasWinnerId: await hasColumn(pool, 'matches', 'WinnerId'),
    matchesHasLoserId: await hasColumn(pool, 'matches', 'LoserId'),
  };
}

export async function createMatchRow(pool, support, { team1Id, team2Id, startTimeUtc, platform, matchType, participantsJson, discipline }) {
  const columns = ['Team1_ID', 'Team2_ID', 'StartTime', 'Status', 'Platform', 'MatchType'];
  const values = [team1Id, team2Id, startTimeUtc, 'live', platform, matchType];

  if (support.matchesHasScore) {
    columns.push('Score');
    values.push('0:0');
  }

  if (support.matchesHasRate) {
    columns.push('Rate');
    values.push('free');
  }

  if (support.matchesHasParticipants) {
    columns.push('Participants');
    values.push(participantsJson);
  }

  if (discipline && support.matchesHasDiscipline) {
    columns.push('Discipline');
    values.push(discipline);
  }

  const placeholders = columns.map(() => '?').join(', ');
  const sql = `INSERT INTO matches (${columns.join(', ')}) VALUES (${placeholders})`;
  try {
    const [result] = await pool.execute(sql, values);
    const id = Number(result.insertId);
    if (id > 0) return id;
    throw new Error('insertId=0 after INSERT into matches');
  } catch (e1) {
    console.error('[mysql] createMatchRow full insert failed, trying bare fallback:', e1.message);
    // Absolute minimal fallback — only columns guaranteed to exist
    try {
      const [result] = await pool.execute(
        'INSERT INTO matches (Team1_ID, Team2_ID, StartTime, Status) VALUES (?, ?, ?, ?)',
        [team1Id, team2Id, startTimeUtc, 'live']
      );
      const id = Number(result.insertId);
      if (id > 0) return id;
      throw new Error('insertId=0 after fallback INSERT into matches');
    } catch (e2) {
      console.error('[mysql] createMatchRow bare fallback also failed:', e2.message);
      throw e2;
    }
  }
}

export async function updateMatchPerSecond(pool, support, matchId, { status, score, participantsJson }) {
  const set = ['Status = ?', 'Score = ?'];
  const params = [status, score];

  if (support.matchesHasParticipants) {
    set.push('Participants = ?');
    params.push(participantsJson);
  }

  const sql = `UPDATE matches SET ${set.join(', ')} WHERE ID = ?`;
  params.push(matchId);
  await pool.execute(sql, params);
}

export async function endMatch(pool, support, matchId, { score, endTimeUtc, winnerUserId, loserUserId }) {
  const set = ["Status = 'end'", 'Score = ?', 'EndTime = ?'];
  const params = [score, endTimeUtc];

  if (support?.matchesHasWinnerId && winnerUserId) {
    set.push('WinnerId = ?');
    params.push(winnerUserId);
  }
  if (support?.matchesHasLoserId && loserUserId) {
    set.push('LoserId = ?');
    params.push(loserUserId);
  }

  params.push(matchId);
  const sql = `UPDATE matches SET ${set.join(', ')} WHERE ID = ?`;
  try {
    await pool.execute(sql, params);
  } catch (e) {
    // Fallback: update only Status+EndTime (Score may be too long for old varchar(20) column)
    console.warn('[mysql] endMatch full update failed, retrying without Score:', e.message);
    await pool.execute(
      "UPDATE matches SET Status = 'end', EndTime = ? WHERE ID = ?",
      [endTimeUtc, matchId]
    );
  }
}

export async function insertMatchTick(pool, matchId, tickTsUtc, stateJson) {
  const sql = `INSERT INTO match_ticks (match_id, tick_time, state_json) VALUES (?, ?, ?)`;
  await pool.execute(sql, [matchId, tickTsUtc, stateJson]);
}

/**
 * Saves the full match result directly to MySQL.
 * Uses INSERT IGNORE on match_key for idempotency — safe to retry.
 * Returns true if this was the first time processing this match, false if already done.
 */
export async function processMatchResult(pool, {
  matchKey, matchId,
  winnerUserId, loserUserId,
  winnerUsername, loserUsername,
  leftUserId, rightUserId,
  leftUsername, rightUsername,
  isDraw,
  score, setsWinner, setsLoser,
  reason, endedAt, payloadJson,
}) {
  const WINNER_REWARD = 0.80;
  const LOSER_REWARD  = 0.20;
  const DRAW_REFUND = 1.00;

  // ── Ensure tables exist (DDL outside transaction) ────────────────────────
  await pool.execute(`CREATE TABLE IF NOT EXISTS match_results (
    id              BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    match_key       VARCHAR(100)    NOT NULL,
    match_id        BIGINT UNSIGNED NULL,
    discipline      VARCHAR(50)     NOT NULL DEFAULT 'ping-pong',
    mode            VARCHAR(50)     NOT NULL DEFAULT '1v1',
    winner_user_id  BIGINT UNSIGNED NOT NULL,
    loser_user_id   BIGINT UNSIGNED NOT NULL,
    winner_username VARCHAR(100)    NOT NULL DEFAULT '',
    loser_username  VARCHAR(100)    NOT NULL DEFAULT '',
    score           VARCHAR(200)    NOT NULL DEFAULT '',
    sets_winner     TINYINT         NOT NULL DEFAULT 0,
    sets_loser      TINYINT         NOT NULL DEFAULT 0,
    reason          VARCHAR(50)     NOT NULL DEFAULT '',
    ended_at        DATETIME        NULL,
    payload_json    LONGTEXT        NULL,
    created_at      TIMESTAMP       NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uniq_match_key (discipline, mode, match_key),
    INDEX idx_winner (winner_user_id),
    INDEX idx_loser  (loser_user_id)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);

  await pool.execute(`CREATE TABLE IF NOT EXISTS match_rewards_log (
    match_key  VARCHAR(100) NOT NULL,
    discipline VARCHAR(50)  NOT NULL DEFAULT 'ping-pong',
    mode       VARCHAR(50)  NOT NULL DEFAULT '1v1',
    created_at TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (discipline, mode, match_key)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);

  await pool.execute(`CREATE TABLE IF NOT EXISTS transactions (
    id          BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    user_id     BIGINT UNSIGNED NOT NULL,
    type        VARCHAR(20)     NOT NULL,
    amount      DECIMAL(12,2)   NOT NULL,
    title       VARCHAR(255)    NOT NULL,
    description TEXT            NULL,
    category    VARCHAR(50)     NULL,
    created_at  TIMESTAMP       NOT NULL DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_user_created (user_id, created_at)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4`);

  // ── Ensure user_stats rows exist ─────────────────────────────────────────
  const insStatsSQL = `INSERT IGNORE INTO user_stats
    (user_id, balance, matches_played, matches_won, matches_lost, matches_draw,
     tournaments_played, tournaments_won, leagues_participated,
     total_income, total_expenses, total_transactions, account_status)
   VALUES (?, 0.00, 0, 0, 0, 0, 0, 0, 0, 0.00, 0.00, 0, 'active')`;
  await pool.execute(insStatsSQL, [winnerUserId]);
  await pool.execute(insStatsSQL, [loserUserId]);
  if (isDraw) {
    await pool.execute(insStatsSQL, [leftUserId]);
    await pool.execute(insStatsSQL, [rightUserId]);
  }

  // ── Ensure discipline_stats rows exist ────────────────────────────────────
  const insDsSQL = `INSERT IGNORE INTO discipline_stats
    (user_id, discipline, mode) VALUES (?, 'ping-pong', '1v1')`;
  await pool.execute(insDsSQL, [winnerUserId]);
  await pool.execute(insDsSQL, [loserUserId]);
  if (isDraw) {
    await pool.execute(insDsSQL, [leftUserId]);
    await pool.execute(insDsSQL, [rightUserId]);
  }

  // ── Transactional write ──────────────────────────────────────────────────
  const conn = await pool.getConnection();
  try {
    await conn.beginTransaction();

    // INSERT IGNORE is idempotent: if match_key already exists, affectedRows = 0
    const [ins] = await conn.execute(
      `INSERT IGNORE INTO match_rewards_log (match_key, discipline, mode) VALUES (?, 'ping-pong', '1v1')`,
      [matchKey]
    );

    // Always upsert match_results (idempotent via UNIQUE KEY on discipline+mode+match_key)
    await conn.execute(
      `INSERT IGNORE INTO match_results
         (match_key, match_id, discipline, mode,
          winner_user_id, winner_username,
          loser_user_id,  loser_username,
          score, sets_winner, sets_loser, reason, ended_at, payload_json)
       VALUES (?, ?, 'ping-pong', '1v1', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
      [
        matchKey,
        matchId || null,
        winnerUserId,
        winnerUsername || '',
        loserUserId,
        loserUsername || '',
        score || '',
        setsWinner != null ? setsWinner : 0,
        setsLoser  != null ? setsLoser  : 0,
        reason || '',
        endedAt || null,
        payloadJson || null,
      ]
    );

    if (ins.affectedRows > 0) {
      // First time processing this match — update global stats + discipline stats + transactions

      const matchLabel = matchId ? `Mecz #${matchId}` : `Mecz ${matchKey}`;
      const txSQL = `INSERT INTO transactions
        (user_id, type, amount, title, description, category)
        VALUES (?, 'income', ?, ?, ?, 'match')`;

      if (isDraw) {
        // Draw settlement: both players get stake refund, both count as draw.
        for (const userId of [leftUserId, rightUserId]) {
          await conn.execute(
            `UPDATE user_stats
             SET matches_played     = matches_played     + 1,
                 matches_draw       = matches_draw       + 1,
                 balance            = balance            + ?,
                 total_income       = total_income       + ?,
                 total_transactions = total_transactions + 1
             WHERE user_id = ?`,
            [DRAW_REFUND, DRAW_REFUND, userId]
          );

          await conn.execute(
            `UPDATE discipline_stats
             SET matches_played = matches_played + 1,
                 matches_draw   = matches_draw   + 1,
                 total_income   = total_income   + ?
             WHERE user_id = ? AND discipline = 'ping-pong' AND mode = '1v1'`,
            [DRAW_REFUND, userId]
          );

          await conn.execute(
            txSQL,
            [userId, DRAW_REFUND, 'Ping-Pong 1v1 - remis (zwrot stawki)', `${matchLabel} | ${score}`]
          );
        }
      } else {
        // Standard winner/loser settlement
        await conn.execute(
          `UPDATE user_stats
           SET matches_played     = matches_played     + 1,
               matches_won        = matches_won        + 1,
               balance            = balance            + ?,
               total_income       = total_income       + ?,
               total_transactions = total_transactions + 1
           WHERE user_id = ?`,
          [WINNER_REWARD, WINNER_REWARD, winnerUserId]
        );

        await conn.execute(
          `UPDATE user_stats
           SET matches_played     = matches_played     + 1,
               matches_lost       = matches_lost       + 1,
               balance            = balance            + ?,
               total_income       = total_income       + ?,
               total_transactions = total_transactions + 1
           WHERE user_id = ?`,
          [LOSER_REWARD, LOSER_REWARD, loserUserId]
        );

        await conn.execute(
          `UPDATE discipline_stats
           SET matches_played = matches_played + 1,
               matches_won    = matches_won    + 1,
               total_income   = total_income   + ?
           WHERE user_id = ? AND discipline = 'ping-pong' AND mode = '1v1'`,
          [WINNER_REWARD, winnerUserId]
        );

        await conn.execute(
          `UPDATE discipline_stats
           SET matches_played = matches_played + 1,
               matches_lost   = matches_lost   + 1,
               total_income   = total_income   + ?
           WHERE user_id = ? AND discipline = 'ping-pong' AND mode = '1v1'`,
          [LOSER_REWARD, loserUserId]
        );

        await conn.execute(txSQL, [winnerUserId, WINNER_REWARD, 'Ping-Pong 1v1 - wygrana', `${matchLabel} | ${score}`]);
        await conn.execute(txSQL, [loserUserId,  LOSER_REWARD,  'Ping-Pong 1v1 - udział',  `${matchLabel} | ${score}`]);
      }
    }

    await conn.commit();
    return ins.affectedRows > 0;
  } catch (e) {
    await conn.rollback();
    throw e;
  } finally {
    conn.release();
  }
}
