
import React from 'react';
import { ContentPage } from '../../components/ContentPage';
import { InfoPanel } from '../../components/InfoPanel';
import { ApiSection } from '../../types';

const observerApi: ApiSection[] = [
  {
    title: 'Chemical.Observer',
    description: 'Creates an observer that reacts to changes in a stateful source (Value, Table, Map, or Computed). It allows you to register callbacks that fire when the source changes.',
    entries: [
      {
        signature: 'Chemical.Observer<T>(sourceObject: Stateful<T>): Observer<T>',
        description: 'Constructs a new Observer for the given stateful source.',
        parameters: [
          { name: 'sourceObject', type: 'Stateful<T>', description: 'The Value, Table, Map, or Computed object to observe.' },
        ],
        returns: { type: 'Observer<T>', description: 'A new Observer object.' },
        example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Map = Chemical.Map
local Observer = Chemical.Observer

local name = Value("Alice")
local nameObserver = Observer(name)

local settings = Map({ theme = "dark" })
local settingsObserver = Observer(settings)
        `,
      },
    ],
  },
  {
    title: 'Observer Methods',
    entries: [
      {
        signature: 'observer:onChange(callback: (newValue: T, oldValue: T) -> ()): Connection',
        description: 'Registers a callback function that will be invoked when the observed source value changes. The callback receives the new and old values.',
        parameters: [
          { name: 'callback', type: '(newValue: T, oldValue: T) -> ()', description: 'The function to call on change.' },
        ],
        returns: { type: 'Connection', description: 'A connection object with a `:disconnect()` method to stop listening.' },
        example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Observer = Chemical.Observer

local age = Value(25)
local ageObs = Observer(age)

local connection = ageObs:onChange(function(newAge, oldAge)
  print("Age changed from " .. oldAge .. " to " .. newAge)
end)

age:set(26) -- Output: Age changed from 25 to 26 (callback fires immediately)

connection:disconnect() -- Stop observing
age:set(27) -- Callback will not fire
        `,
      },
      {
        signature: 'observer:onKVChange(callback: (path: (string|number)[], newValue: any, oldValue: any) -> ()): Connection',
        description: 'If the observed source is a Table or Map, this registers a callback for fine-grained changes to its keys/values. The callback receives the path (array of keys/indices) to the changed element, the new value at that path, and the old value. This is useful for deep comparison of table structures.',
        parameters: [
          { name: 'callback', type: '(path, newValue, oldValue) -> ()', description: 'The function to call on key-value change.' },
        ],
        returns: { type: 'Connection', description: 'A connection object with a `:disconnect()` method.' },
        example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Map = Chemical.Map
local Observer = Chemical.Observer

local userProfile = Map({
  name = "Bob",
  details = { "email", "phone" } -- Plain table for details
})
local profileObs = Observer(userProfile)

profileObs:onKVChange(function(path, newValue, oldValue)
  print("Profile changed at path:", table.concat(path, "."), "from", oldValue, "to", newValue)
end)

userProfile:key("name", "Robert")
-- Output: Profile changed at path: name from Bob to Robert (callback fires immediately)

-- To observe changes within the 'details' plain table, you'd typically
-- replace the 'details' table itself or use a Chemical.Table if 'details' needed its own reactivity.
local newDetails = table.clone(userProfile:get().details)
newDetails[1] = "new_email@example.com"
userProfile:key("details", newDetails)
-- Output: Profile changed at path: details from table: XXX to table: YYY (actual addresses will vary)
-- And then another event for the specific change if the new table is compared deeply:
-- Output: Profile changed at path: details.1 from email to new_email@example.com
        `,
        notes: "This method is only effective if the source object's value is a table/map and it's marked for deep comparison (Chemical handles this for Table/Map types)."
      },
      {
        signature: 'observer:destroy()',
        description: 'Destroys the Observer object, disconnecting all its registered callbacks and cleaning up resources.',
        example: `
local Chemical = require(game.ReplicatedStorage.Chemical)
local Value = Chemical.Value
local Observer = Chemical.Observer

local status = Value("active")
local statusObs = Observer(status)
statusObs:onChange(function() print("Status updated") end)

-- ... later
statusObs:destroy()
status:set("inactive") -- The onChange callback will no longer fire
        `,
      },
    ],
  },
];

export const ObserverPage: React.FC = () => {
  return (
    <ContentPage title="Chemical.Observer" sections={observerApi}>
      <InfoPanel type="note" title="Execution Timing">
        <p>
          Callbacks registered with an Observer (e.g., <code>observer:onChange()</code>, <code>observer:onKVChange()</code>)
          are executed <strong>immediately and synchronously</strong> when the observed source's state is
          updated (e.g., via <code>:set()</code> for a <code>Value</code>, or through relevant methods for <code>Table</code>/<code>Map</code>).
        </p>
      </InfoPanel>
    </ContentPage>
  );
};
