import { useState } from 'react'

import { useAuth } from "react-oidc-context"

import { useBrewers, useCoffees, useGrinders, useMethods } from './api';
import { queryClient } from './main';
import { environment } from './environment';

import './App.css'

function Home() {
  const auth = useAuth();
  const token = auth.user?.access_token
  const { isPending: areCoffeesPending, data: coffees } = useCoffees(token!)
  const { isPending: areGrindersPending, data: grinders } = useGrinders(token!)
  const { isPending: areBrewersPending, data: brewers } = useBrewers(token!)
  const { isPending: areMethodsPending, data: methods } = useMethods(token!)

  const [addCoffeeName, setAddCoffeeName] = useState('')
  const [addCoffeeRoastLevel, setAddCoffeeRoastLevel] = useState(3)
  const [addCoffeeNotes, setAddCoffeeNotes] = useState('')

  const [addGrinderName, setAddGrinderName] = useState('')
  const [addGrinderNotes, setAddGrinderNotes] = useState('')

  const [addBrewerName, setAddBrewerName] = useState('')
  const [addBrewerNotes, setAddBrewerNotes] = useState('')

  const [addMethodCoffeeId, setAddMethodCoffeeId] = useState('')
  const [addMethodCoffeeAmountG, setAddMethodCoffeeAmountG] = useState(15)
  const [addMethodGrinderId, setAddMethodGrinderId] = useState('')
  const [addMethodGrindSetting, setAddMethodGrindSetting] = useState('')
  const [addMethodBrewerId, setAddMethodBrewerId] = useState('')
  const [addMethodWaterAmountMl, setAddMethodWaterAmountMl] = useState(250)
  const [addMethodWaterTemperatureC, setAddMethodWaterTemperatureC] = useState(90)
  const [addMethodBloomingTimeSec, setAddMethodBloomingTimeSec] = useState(45)
  const [addMethodBrewingTimeSec, setAddMethodBrewingTimeSec] = useState(120)
  const [addMethodNotes, setAddMethodNotes] = useState('')

  const [randomMix, setRandomMix] = useState(
    undefined as {coffeeId: string, grinderId: string, brewerId: string} | undefined
  )
  const [randomMethodId, setRandomMethodId] = useState(undefined as string | undefined)

  if (areCoffeesPending || areGrindersPending || areBrewersPending || areMethodsPending) {
    return (
      <div>
        Loading...
      </div>
    )
  }

  function openDialog(dialogId: string) {
    const dialogElement = document.getElementById(dialogId);
    (dialogElement as HTMLDialogElement).showModal()
  }

  function closeDialog(dialogId: string) {
    const dialogElement = document.getElementById(dialogId);
    (dialogElement as HTMLDialogElement).close()
  }

  function closeDialogButton(dialogId: string) {
    return <button onClick={closeDialog.bind(null, dialogId)}>Cancel</button>
  }

  async function postNewEntry(entryType: 'coffee'|'grinder'|'brewer'|'method', body: any) {
    try {
      let result = await fetch(
        `${environment.platformRoot}/api/${entryType}`,
        {
          method: 'POST',
          body: JSON.stringify(body),
          headers: {
            Authorization: `Bearer ${token}`,
            'content-type': 'application/json'
          }
        }
      )
      if (result.status !== 200) {
        window.alert(`Error: status ${result.status}; ${await result.text()}`)
      }
    } catch (error) {
      window.alert(`Error: ${error}`)
    }
  }

  // ============================================
  //   COFFEE
  // ============================================
  const addCoffeeDialogId = 'addCoffeeDialog'
  async function postNewCoffee() {
    postNewEntry(
      'coffee',
      {
        name: addCoffeeName,
        roastLevel: addCoffeeRoastLevel,
        notes: addCoffeeNotes,
      }
    )
    queryClient.invalidateQueries({ queryKey: ['coffees'] })
    closeDialog(addCoffeeDialogId)
  }
  function renderAddCoffeeDialog() {
    return (
      <dialog id={addCoffeeDialogId} className='addSomethingDialog'>
        <div className='column'>
          <label className="" htmlFor="addCoffeeName">
            Name
          </label>
          <input className="" id="addCoffeeName" name="addCoffeeName" placeholder="Rost Aalto, Fazer Blend..." required type="text" value={addCoffeeName} onChange={(event) => setAddCoffeeName(event.target.value)}/>

          <label className="" htmlFor="addCoffeeRoastLevel">
            Roast level
          </label>
          <input className="" id="addCoffeeRoastLevel" name="addCoffeeRoastLevel" placeholder="3" required type="number" value={addCoffeeRoastLevel} onChange={(event) => setAddCoffeeRoastLevel(Number.parseInt(event.target.value))}/>

          <label className="" htmlFor="addCoffeeNotes">
            Notes
          </label>
          <input className="" id="addCoffeeNotes" name="addCoffeeNotes" placeholder="Notes" required type="text" value={addCoffeeNotes} onChange={(event) => setAddCoffeeNotes(event.target.value)}/>

          <button onClick={postNewCoffee}>
            Add coffee
          </button>
          {closeDialogButton(addCoffeeDialogId)}
        </div>
      </dialog>
    )
  }

  // ============================================
  //   GRINDER
  // ============================================
  const addGrinderDialogId = 'addGrinderDialog'
  async function postNewGrinder() {
    postNewEntry(
      'grinder',
      {
        name: addGrinderName,
        notes: addGrinderNotes,
      }
    )
    queryClient.invalidateQueries({ queryKey: ['grinders'] })
    closeDialog(addGrinderDialogId)
  }
  function renderAddGrinderDialog() {
    return (
      <dialog id={addGrinderDialogId} className='addSomethingDialog'>
        <div className='column'>
          <label className="" htmlFor="addGrinderName">
            Name
          </label>
          <input className="" id="addGrinderName" name="addGrinderName" placeholder="Comandante C40" required type="text" value={addGrinderName} onChange={(event) => setAddGrinderName(event.target.value)}/>

          <label className="" htmlFor="addGrinderNotes">
            Notes
          </label>
          <input className="" id="addGrinderNotes" name="addGrinderNotes" placeholder="Notes" required type="text" value={addGrinderNotes} onChange={(event) => setAddGrinderNotes(event.target.value)}/>

          <button onClick={postNewGrinder}>
            Add grinder
          </button>
          {closeDialogButton(addGrinderDialogId)}
        </div>
      </dialog>
    )
  }

  // ============================================
  //   BREWER
  // ============================================
  const addBrewerDialogId = 'addBrewerDialog'
  async function postNewBrewer() {
    postNewEntry(
      'brewer',
      {
        name: addBrewerName,
        notes: addBrewerNotes,
      }
    )
    queryClient.invalidateQueries({ queryKey: ['brewers'] })
    closeDialog(addBrewerDialogId)
  }
  function renderAddBrewerDialog() {
    return (
      <dialog id={addBrewerDialogId} className='addSomethingDialog'>
        <div className='column'>
          <label className="" htmlFor="addBrewerName">
            Name
          </label>
          <input className="" id="addBrewerName" name="addBrewerName" placeholder="Bialetti Moka Express, Hario Switch..." required type="text" value={addBrewerName} onChange={(event) => setAddBrewerName(event.target.value)}/>

          <label className="" htmlFor="addBrewerNotes">
            Notes
          </label>
          <input className="" id="addBrewerNotes" name="addBrewerNotes" placeholder="Notes" required type="text" value={addBrewerNotes} onChange={(event) => setAddBrewerNotes(event.target.value)}/>

          <button onClick={postNewBrewer}>
            Add brewer
          </button>
          {closeDialogButton(addBrewerDialogId)}
        </div>
      </dialog>
    )
  }

  // ============================================
  //   METHOD
  // ============================================
  const addMethodDialogId = 'addMethodDialog'
  async function postNewMethod() {
    postNewEntry(
      'method',
      {
        coffeeId: addMethodCoffeeId,
        coffeeAmountG: addMethodCoffeeAmountG,
        grinderId: addMethodGrinderId,
        grindSetting: addMethodGrindSetting,
        brewerId: addMethodBrewerId,
        waterAmountMl: addMethodWaterAmountMl,
        waterTemperatureC: addMethodWaterTemperatureC,
        bloomingTimeSec: addMethodBloomingTimeSec,
        brewingTimeSec: addMethodBrewingTimeSec,
        notes: addMethodNotes,
      }
    )
    queryClient.invalidateQueries({ queryKey: ['methods'] })
    closeDialog(addMethodDialogId)
  }
  function renderAddMethodDialog() {
    const coffeeOptions = coffees.map((coffee: any) => <option key={`coffee-option-${coffee.id}`} value={coffee.id}>{coffee.name} - Roast {coffee.roast_level}</option>)
    const grinderOptions = grinders.map((grinder: any) => <option key={`grinder-option-${grinder.id}`} value={grinder.id}>{grinder.name}</option>)
    const brewerOptions = brewers.map((brewer: any) => <option key={`brewer-option-${brewer.id}`} value={brewer.id}>{brewer.name}</option>)
    return (
      <dialog id={addMethodDialogId} className='addSomethingDialog'>
        <div className='column'>
          <label className="" htmlFor="addMethodCoffee">
            Coffee
          </label>
          <select id="addMethodCoffee" name="addMethodCoffee" value={addMethodCoffeeId} onChange={(event) => setAddMethodCoffeeId(event.target.value)}>
            <option value="">Select a coffee</option>
            {coffeeOptions}
          </select>

          <label className="" htmlFor="addMethodCoffeeAmountG">
            Coffee amount (grams)
          </label>
          <input className="" id="addMethodCoffeeAmountG" name="addMethodCoffeeAmountG" placeholder="15" required type="number" value={addMethodCoffeeAmountG} onChange={(event) => setAddMethodCoffeeAmountG(Number.parseInt(event.target.value))}/>

          <label className="" htmlFor="addMethodGrinder">
            Grinder
          </label>
          <select id="addMethodGrinder" name="addMethodGrinder" value={addMethodGrinderId} onChange={(event) => setAddMethodGrinderId(event.target.value)}>
            <option value="">Select a grinder</option>
            {grinderOptions}
          </select>

          <label className="" htmlFor="addMethodGrindSetting">
            Grind setting
          </label>
          <input className="" id="addMethodGrindSetting" name="addMethodGrindSetting" placeholder="Clicks" required type="text" value={addMethodGrindSetting} onChange={(event) => setAddMethodGrindSetting(event.target.value)}/>

          <label className="" htmlFor="addMethodBrewer">
            Brewer
          </label>
          <select id="addMethodBrewer" name="addMethodBrewer" value={addMethodBrewerId} onChange={(event) => setAddMethodBrewerId(event.target.value)}>
            <option value="">Select a brewer</option>
            {brewerOptions}
          </select>

          <label className="" htmlFor="addMethodWaterAmountMl">
            Water amount (ml)
          </label>
          <input className="" id="addMethodWaterAmountMl" name="addMethodWaterAmountMl" placeholder="250" required type="number" value={addMethodWaterAmountMl} onChange={(event) => setAddMethodWaterAmountMl(Number.parseInt(event.target.value))}/>

          <label className="" htmlFor="addMethodWaterTemperatureC">
            Water temperature (°C)
          </label>
          <input className="" id="addMethodWaterTemperatureC" name="addMethodWaterTemperatureC" placeholder="250" required type="number" value={addMethodWaterTemperatureC} onChange={(event) => setAddMethodWaterTemperatureC(Number.parseInt(event.target.value))}/>

          <label className="" htmlFor="addMethodBloomingTimeSec">
            Bloom time (sec)
          </label>
          <input className="" id="addMethodBloomingTimeSec" name="addMethodBloomingTimeSec" placeholder="45" required type="number" value={addMethodBloomingTimeSec} onChange={(event) => setAddMethodBloomingTimeSec(Number.parseInt(event.target.value))}/>

          <label className="" htmlFor="addMethodBrewingTimeSec">
            Brew time (sec)
          </label>
          <input className="" id="addMethodBrewingTimeSec" name="addMethodBrewingTimeSec" placeholder="120" required type="number" value={addMethodBrewingTimeSec} onChange={(event) => setAddMethodBrewingTimeSec(Number.parseInt(event.target.value))}/>

          <label className="" htmlFor="addMethodNotes">
            Notes
          </label>
          <input className="" id="addMethodNotes" name="addMethodNotes" placeholder="Notes" required type="text" value={addMethodNotes} onChange={(event) => setAddMethodNotes(event.target.value)}/>

          <button onClick={postNewMethod}>
            Add method
          </button>
          {closeDialogButton(addMethodDialogId)}
        </div>
      </dialog>
    )
  }

  return (
    <div className='column'>
      <div className='mixOptions'>
        {renderAddCoffeeDialog()}
        <button className='coffees' onClick={openDialog.bind(null, addCoffeeDialogId)}>
          Add coffee
        </button>
        {coffees?.map((coffee: any) => <p key={`coffee-${coffee.id}`} className={`coffees ${coffee.id === randomMix?.coffeeId ? 'randomlySelected' : ''}`}>{coffee.name} - Roast {coffee.roast_level}</p>)}
        {renderAddGrinderDialog()}
        <button className='grinders' onClick={openDialog.bind(null, addGrinderDialogId)}>
          Add grinder
        </button>
        {grinders?.map((grinder: any) => <p key={`grinder-${grinder.id}`} className={`grinders ${grinder.id === randomMix?.grinderId ? 'randomlySelected' : ''}`}>{grinder.name}</p>)}
        {renderAddBrewerDialog()}
        <button className='brewers' onClick={openDialog.bind(null, addBrewerDialogId)}>
          Add brewer
        </button>
        {brewers?.map((brewer: any) => <p key={`brewer-${brewer.id}`} className={`brewers ${brewer.id === randomMix?.brewerId ? 'randomlySelected' : ''}`}>{brewer.name}</p>)}
      </div>
      <div className='row'>
      </div>
      <div className='column methods'>
        {renderAddMethodDialog()}
        <button onClick={openDialog.bind(null, addMethodDialogId)}>
          Add method
        </button>
        <table className='methods'>
          <tbody>
            {methods?.map((method: any) => {
              const coffeeFound = coffees.find((coffee: any) => coffee.id === method.coffee_id)
              const grinderFound = grinders.find((grinder: any) => grinder.id === method.grinder_id)
              const brewerFound = brewers.find((brewer: any) => brewer.id === method.brewer_id)
              const isRandomlySelected = (randomMethodId && method.id === randomMethodId)
                || (randomMix && method.coffee_id === randomMix?.coffeeId && method.grinder_id === randomMix?.grinderId && method.brewer_id === randomMix?.brewerId)
              return (
                <tr key={`method-${method.id}`} className={`${isRandomlySelected ? 'randomlySelected' : ''}`}>
                  <td>{coffeeFound.name}</td>
                  <td>{method.coffee_amount_g} g</td>
                  <td>{grinderFound.name}</td>
                  <td>{method.grind_setting}</td>
                  <td>{brewerFound.name}</td>
                  <td>{method.water_amount_ml} ml</td>
                  <td>{method.water_temperature_c} °C</td>
                  <td>Bloom {method.blooming_time_sec} sec</td>
                  <td>Brew {method.brewing_time_sec} sec</td>
                </tr>
              )
            })}
          </tbody>
        </table>
        <div className="row randomizerButtons">
          <button onClick={() => {
            setRandomMethodId(undefined)
            const coffeeIndex = Math.floor(Math.random() * coffees.length)
            const grinderIndex = Math.floor(Math.random() * grinders.length)
            const brewerIndex = Math.floor(Math.random() * brewers.length)
            setRandomMix({
              coffeeId: coffees[coffeeIndex]?.id,
              grinderId: grinders[grinderIndex]?.id,
              brewerId: brewers[brewerIndex]?.id
            })
          }}>
            Select a random mix
          </button>
          <button onClick={() => {
            setRandomMix(undefined)
            const randomIndex = Math.floor(Math.random() * methods.length)
            setRandomMethodId(methods[randomIndex]?.id)
          }}>
            Select a random method
          </button>
        </div>
      </div>
    </div>
  )
}

function App() {
  const auth = useAuth();

  switch (auth.activeNavigator) {
    case "signinSilent":
    return <div>Signing you in...</div>;
    case "signoutRedirect":
    return <div>Signing you out...</div>;
  }

  if (auth.isLoading) {
    return <div>Loading...</div>;
  }

  if (auth.error) {
    return <div>Oops... {auth.error.message}</div>;
  }

  if (auth.isAuthenticated) {
    if (document.location.search.includes('code=') && document.location.search.includes('state=')) {
      document.location.href = '/'
      return
    }

    return (
      <div className='layout'>
        <nav className='row'>
          <h1>kahvi.juustodiilerit.fi</h1>
          <button onClick={() => void auth.removeUser()}>Log out</button>
        </nav>
        <main className="column">
          <Home/>
        </main>
      </div>
    );
  }

  return (
    <div className='layout'>
      <nav className='row'>
        <h1>kahvi.juustodiilerit.fi</h1>
        <button onClick={() => void auth.signinRedirect()}>Log in</button>
      </nav>
      <main className="column">
        <button onClick={() => void auth.signinRedirect()}>Log in</button>
      </main>
    </div>

  )
}

export default App
