import 'dotenv/config';
import express from 'express';
import cors from 'cors';
import { createClient } from '@supabase/supabase-js';
import * as decksApi from '../src/lib/api/decks-core.js';

const app = express();
app.use(cors({ origin: true, credentials: true }));
app.use(express.json());

const supabaseUrl = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || process.env.VITE_SUPABASE_ANON_KEY;
if (!supabaseUrl || !supabaseAnonKey) {
  console.warn('Missing SUPABASE_URL or SUPABASE_ANON_KEY (or VITE_*). API may fail.');
}

function supabaseClient(accessToken = null) {
  const options = { db: { schema: 'omotomo' } };
  if (accessToken) {
    options.global = { headers: { Authorization: `Bearer ${accessToken}` } };
  }
  return createClient(supabaseUrl ?? '', supabaseAnonKey ?? '', options);
}

const anonSupabase = () => supabaseClient();

async function resolveEmailFromUsername(username) {
  const trimmed = (username ?? '').trim();
  if (!trimmed) return null;
  const { data, error } = await anonSupabase()
    .from('profiles')
    .select('email')
    .ilike('display_name', trimmed)
    .limit(1);
  if (error || !data?.length || !data[0]?.email) return null;
  return data[0].email;
}

function requireAuth(req, res, next) {
  const authHeader = req.headers.authorization;
  const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
  if (!token) {
    res.status(401).json({ error: 'Missing or invalid Authorization header' });
    return;
  }
  const supabase = supabaseClient(token);
  supabase.auth.getUser(token).then(({ data: { user }, error }) => {
    if (error || !user) {
      res.status(401).json({ error: 'Invalid or expired token' });
      return;
    }
    req.supabase = supabase;
    req.userId = user.id;
    next();
  });
}

function optionalAuth(req, res, next) {
  const authHeader = req.headers.authorization;
  const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;
  if (!token) {
    req.supabase = anonSupabase();
    req.userId = null;
    next();
    return;
  }
  const supabase = supabaseClient(token);
  supabase.auth.getUser(token).then(({ data: { user }, error }) => {
    req.supabase = supabase;
    req.userId = error || !user ? null : user.id;
    next();
  });
}

app.post('/api/auth/login', express.json(), async (req, res) => {
  try {
    const { email_or_username, password } = req.body || {};
    if (!email_or_username || !password) {
      res.status(400).json({ error: 'email_or_username and password required' });
      return;
    }
    let email = String(email_or_username).trim();
    if (!email.includes('@')) {
      const resolved = await resolveEmailFromUsername(email);
      if (!resolved) {
        res.status(401).json({ error: 'No account with that username' });
        return;
      }
      email = resolved;
    }
    const { data, error } = await anonSupabase().auth.signInWithPassword({ email, password });
    if (error) {
      res.status(401).json({ error: error.message });
      return;
    }
    res.json({
      access_token: data.session?.access_token,
      refresh_token: data.session?.refresh_token,
      expires_at: data.session?.expires_at,
      user: data.user,
    });
  } catch (e) {
    res.status(500).json({ error: e?.message ?? 'Login failed' });
  }
});

app.post('/api/auth/register', express.json(), async (req, res) => {
  try {
    const { email, password, display_name } = req.body || {};
    if (!email || !password) {
      res.status(400).json({ error: 'email and password required' });
      return;
    }
    const { data, error } = await anonSupabase().auth.signUp({
      email: String(email).trim(),
      password,
      options: { data: { display_name: (display_name ?? '').trim() || null } },
    });
    if (error) {
      res.status(400).json({ error: error.message });
      return;
    }
    const needsConfirmation = data?.user && !data?.session;
    res.status(201).json({
      access_token: data.session?.access_token ?? null,
      refresh_token: data.session?.refresh_token ?? null,
      expires_at: data.session?.expires_at ?? null,
      user: data.user,
      needs_confirmation: needsConfirmation,
    });
  } catch (e) {
    res.status(500).json({ error: e?.message ?? 'Registration failed' });
  }
});

app.get('/api/auth/session', requireAuth, (req, res) => {
  res.json({ user: { id: req.userId } });
});

app.get('/api/decks/mine', requireAuth, async (req, res) => {
  try {
    const decks = await decksApi.fetchMyDecks(req.supabase, req.userId);
    res.json(decks);
  } catch (e) {
    res.status(500).json({ error: e?.message ?? 'Failed to load decks' });
  }
});

app.get('/api/decks/published', optionalAuth, async (req, res) => {
  try {
    const decks = await decksApi.fetchPublishedDecks(req.supabase, req.userId ?? undefined);
    res.json(decks);
  } catch (e) {
    res.status(500).json({ error: e?.message ?? 'Failed to load decks' });
  }
});

app.get('/api/decks/:id', optionalAuth, async (req, res) => {
  try {
    const deck = await decksApi.fetchDeckWithQuestions(req.supabase, req.params.id);
    const isMyCopy = !!(req.userId && deck.owner_id === req.userId && deck.copied_from_deck_id);
    const isOwn = !!(req.userId && deck.owner_id === req.userId && !deck.copied_from_deck_id);
    const canView = deck.published || isMyCopy || isOwn;
    if (!canView) {
      res.status(404).json({ error: 'This deck is not available' });
      return;
    }
    res.json(deck);
  } catch (e) {
    if (e?.message === 'Deck not found') {
      res.status(404).json({ error: e.message });
      return;
    }
    res.status(500).json({ error: e?.message ?? 'Failed to load deck' });
  }
});

app.post('/api/decks', requireAuth, express.json(), async (req, res) => {
  try {
    const { title, description, config, questions } = req.body || {};
    if (!title || typeof title !== 'string' || !title.trim()) {
      res.status(400).json({ error: 'title required' });
      return;
    }
    const normalized = Array.isArray(questions)
      ? questions
          .map((q) => {
            const answers = (q.answers ?? []).map((a) => String(a).trim()).filter(Boolean);
            if (answers.length === 0) return null;
            const indices = (q.correct_answer_indices ?? [0]).filter((i) => i >= 0 && i < answers.length);
            return {
              prompt: (q.prompt ?? '').trim(),
              explanation: (q.explanation ?? '').trim() || null,
              answers,
              correct_answer_indices: indices.length ? indices : [0],
            };
          })
          .filter(Boolean)
      : [];
    if (normalized.length === 0) {
      res.status(400).json({ error: 'At least one question with one answer required' });
      return;
    }
    const id = await decksApi.createDeck(req.supabase, req.userId, {
      title: title.trim(),
      description: (description ?? '').trim(),
      config: config ?? {},
      questions: normalized,
    });
    res.status(201).json({ id });
  } catch (e) {
    res.status(500).json({ error: e?.message ?? 'Failed to create deck' });
  }
});

app.patch('/api/decks/:id', requireAuth, express.json(), async (req, res) => {
  try {
    const full = await decksApi.fetchDeckWithQuestions(req.supabase, req.params.id);
    if (full.owner_id !== req.userId) {
      res.status(404).json({ error: 'Deck not found' });
      return;
    }
    const { title, description, config, questions } = req.body || {};
    const newTitle = title !== undefined ? String(title).trim() : (full.title ?? '');
    const newDesc = description !== undefined ? String(description).trim() : (full.description ?? '');
    const newConfig = config !== undefined ? (config ?? {}) : (full.config ?? {});
    let qList = questions;
    if (qList === undefined) {
      qList = (full.questions ?? []).map((q) => ({
        prompt: q.prompt ?? '',
        explanation: q.explanation ?? '',
        answers: q.answers ?? [],
        correct_answer_indices: q.correct_answer_indices ?? [0],
      }));
    } else {
      qList = Array.isArray(qList)
        ? qList
            .map((q) => {
              const answers = (q.answers ?? []).map((a) => String(a).trim()).filter(Boolean);
              if (answers.length === 0) return null;
              const indices = (q.correct_answer_indices ?? [0]).filter((i) => i >= 0 && i < answers.length);
              return {
                prompt: (q.prompt ?? '').trim(),
                explanation: (q.explanation ?? '').trim() || null,
                answers,
                correct_answer_indices: indices.length ? indices : [0],
              };
            })
            .filter(Boolean)
        : [];
      if (qList.length === 0) {
        res.status(400).json({ error: 'At least one question with one answer required' });
        return;
      }
    }
    await decksApi.updateDeck(req.supabase, req.params.id, {
      title: newTitle,
      description: newDesc,
      config: newConfig,
      questions: qList,
    });
    res.json({ ok: true });
  } catch (e) {
    if (e?.message === 'Deck not found') {
      res.status(404).json({ error: e.message });
      return;
    }
    res.status(500).json({ error: e?.message ?? 'Failed to update deck' });
  }
});

app.post('/api/decks/:id/publish', requireAuth, async (req, res) => {
  try {
    const { data: deck } = await req.supabase.from('decks').select('owner_id').eq('id', req.params.id).single();
    if (!deck || deck.owner_id !== req.userId) {
      res.status(404).json({ error: 'Deck not found' });
      return;
    }
    await decksApi.togglePublished(req.supabase, req.params.id, true);
    res.json({ ok: true });
  } catch (e) {
    res.status(500).json({ error: e?.message ?? 'Failed to publish' });
  }
});

app.post('/api/decks/:id/copy', requireAuth, async (req, res) => {
  try {
    const id = await decksApi.copyDeckToUser(req.supabase, req.params.id, req.userId);
    res.status(201).json({ id });
  } catch (e) {
    if (e?.message?.includes('not available to copy')) {
      res.status(400).json({ error: e.message });
      return;
    }
    res.status(500).json({ error: e?.message ?? 'Failed to copy deck' });
  }
});

app.get('/api/decks/:id/update-preview', requireAuth, async (req, res) => {
  try {
    const preview = await decksApi.getSourceUpdatePreview(req.supabase, req.params.id, req.userId);
    res.json(preview);
  } catch (e) {
    if (e?.message?.includes('not found or not a copy')) {
      res.status(404).json({ error: e.message });
      return;
    }
    res.status(500).json({ error: e?.message ?? 'Failed to load preview' });
  }
});

app.post('/api/decks/:id/apply-update', requireAuth, async (req, res) => {
  try {
    await decksApi.applySourceUpdate(req.supabase, req.params.id, req.userId);
    res.json({ ok: true });
  } catch (e) {
    if (e?.message?.includes('not found or not a copy') || e?.message?.includes('no longer available')) {
      res.status(400).json({ error: e.message });
      return;
    }
    res.status(500).json({ error: e?.message ?? 'Failed to apply update' });
  }
});

const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
  console.log(`API server listening on http://localhost:${PORT}`);
});
