package dev.cf.recipe;

import dev.cf.Tones;

import dev.crossfly.device.Device;
import dev.crossfly.sqlite.SqliteCursor;
import dev.crossfly.sqlite.SqliteDb;
import dev.crossfly.ui.*;
import dev.crossfly.ui.color.*;
import dev.crossfly.ui.graphics.*;
import dev.crossfly.ui.layout.*;
import dev.crossfly.ui.style.*;

import static dev.crossfly.ui.Widgets.*;

/**
 * Storing something, on every platform, with no ORM and no generated code.
 *
 * <p>{@link SqliteDb} is the whole data layer — native on desktop, Android and iOS, and WASM on the
 * web, so the same four calls work everywhere. {@code exec} for DDL, {@code execute} for a
 * parameterised write, {@code query} for a cursor, and {@code transaction} to make several writes
 * one thing.</p>
 *
 * <p>Two habits worth copying from the first line you write. <b>Bind parameters, never concatenate
 * SQL</b> — the {@code ?} form is not only safe, it lets SQLite reuse the prepared statement.
 * And <b>name your columns in the SELECT</b>: the cursor reads by position, so a {@code SELECT *}
 * silently shifts every getter the day somebody adds a column.</p>
 */
public class R18Storage extends Recipe
{
   private SqliteDb db;
   private TextField input;
   private Component list;

   public String title()
   {
      return "Storing something";
   }

   public String note()
   {
      return "Add a few notes and reopen the recipe — they are still there. The same code runs on"
         + " desktop, Android, iOS and the web.";
   }

   public void build(Component body) throws Exception
   {
      open();

      Component row = new Component();
      row.setLayout(new GridLayout(Position.START, Position.CENTER));
      input = new TextField();
      input.caption("A note");
      input.testTag("store.input");
      Button add = new Button(MaterialIcon.of(0xE145, 1.4f));   // add
      add.getStyle()
         .backColor(Tones.accent())
         .iconColor(Styles.colorScheme().onPrimary)
         .borderColor(0);
      add.testTag("store.add");
      add.onAction(c -> save());
      row.add(1f, 0f, input, outgap(add, .5f, 0, 0, 0));
      body.add(LAY_AUTO, outgap(row, 0, 0, 0, .8f));

      list = new Component();
      list.setLayout(new VerticalLayout(Position.START, Position.START));
      list.testTag("store.list");
      body.add(LAY_AUTO, list);
      reload();
   }

   private void open()
   {
      String base = Device.homeFolder;
      db = SqliteDb.open(base == null || base.isEmpty() ? "cf-notes.db" : base + "/cf-notes.db");
      db.exec("CREATE TABLE IF NOT EXISTS note(id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT)");
   }

   private void save()
   {
      String t = input.getText()
         .trim();
      if (t.isEmpty())
         return;
      db.execute("INSERT INTO note(text) VALUES(?)", t);   // bound, never concatenated
      input.setText("");
      reload();
      Screen.repaint();
   }

   private void reload()
   {
      try
      {
         list.removeAll();
         // removeAll() takes the layout with it — put it back before adding anything.
         list.setLayout(new VerticalLayout(Position.START, Position.START));
         // Columns named, so position 0 stays position 0 when the table grows a column.
         try (SqliteCursor c = db.query("SELECT id, text FROM note ORDER BY id DESC LIMIT 12"))
         {
            boolean any = false;
            while (c.next())
            {
               any = true;
               list.add(LAY_AUTO, note(c.getLong(0), c.getString(1)));
            }
            if (!any)
               list.add(LAY_AUTO, label("Nothing stored yet.", 1f, false, Styles.captionColor,
                  Position.START));
         }
         // invalidateMeasure, not setValidLayout(false): the list grew, and the scrolling body
         // around it has to measure it again - or the new note came out cut (iPhone, 17/10/2026).
         list.invalidateMeasure();
      }
      catch (Exception e)
      {
         dev.crossfly.util.Debug.handle(e);
      }
   }

   private Component note(final long id, String text) throws Exception
   {
      Card c = new Card();
      c.setLayout(new GridLayout(Position.START, Position.CENTER));
      c.getStyle()
         .outgap(Gap.ofFont(0, 0, 0, .4f));
      Button del = new Button(MaterialIcon.of(0xE872, 1.2f));   // delete
      del.getStyle()
         .iconColor(Tones.danger())
         .borderColor(0)
         .backColor(0);
      del.onAction(e ->
      {
         db.execute("DELETE FROM note WHERE id=?", id);
         reload();
         Screen.repaint();
      });
      c.add(1f, 0f, label(text, 1f, false, Styles.textColor, Position.START), del);
      return c;
   }

   /** Closing when the screen goes away — a connection is a resource, not a value. */
   @Override
   public void onRemove()
   {
      super.onRemove();
      if (db != null)
      {
         db.close();
         db = null;
      }
   }
}
