import HTTP

"""Pluto Events interface

Use this interface to hook up functionality into the Pluto world.
Events are guaranteed to be run at least every time something interesting happens,
but keep in mind that Pluto may be run the events multiple times "logically". For instance,
the FileSaveEvent may be triggered whenever pluto wants to make sure the file is saved, 
which may be more often than the file is actually changed. Deduplicate on your own if
you care about this.

Define your function to handle the events using multiple dispatch:

First assign a handler for all the types you will not use, using the supertype:

```julia-repl
julia> function myfn(a::PlutoEvent)
            nothing
        end
```


And then create a special function for each event you want to handle specially

```julia-repl
julia> function myfn(a::FileSaveEvent)
            HTTP.post("https://my.service.com/count_saves")
        end
```

Finally, pass the listener to Pluto's configurations with a keyword argument

```julia-repl
julia> Pluto.run(; on_event = myfn)
```
"""
abstract type PlutoEvent end

function try_event_call(session, event::PlutoEvent)
    return try
        session.options.server.on_event(event)
    catch e
        @warn "Couldn't run event listener" event exception=(e, catch_backtrace())
        nothing
    end
end


# Triggered when the web server gets started
struct ServerStartEvent <: PlutoEvent
    address::String
    port::UInt16
end

# Triggered when a notebook is saved
struct FileSaveEvent <: PlutoEvent
    notebook::Notebook
    file_contents::String
    path::String
end

FileSaveEvent(notebook::Notebook) = begin
    file_contents = sprint() do io
        save_notebook(io, notebook)
    end
    FileSaveEvent(notebook, file_contents, notebook.path)
end

# Triggered when the local code has changed (user typed something),
# but the code hasn't run yet. 
struct FileEditEvent <: PlutoEvent
    notebook::Notebook
    file_contents::String
    path::String
end

FileEditEvent(notebook::Notebook) = begin
    file_contents = sprint() do io
        # TODO: https://github.com/fonsp/Pluto.jl/pull/1729: serialize_temp flag
        # to only get local changes; the workspace edit of the notebook!
        save_notebook(io, notebook #=; serialize_temp=true =#)
    end
    FileEditEvent(notebook, file_contents, notebook.path)
end

# Triggered when we open a new notebook
struct NewNotebookEvent <: PlutoEvent
end

# Triggered when a user opens a notebook
struct OpenNotebookEvent <: PlutoEvent
    notebook::Notebook
end

# This will be fired ONLY if URL params don't match anything else.
# Useful if you want to create a file in a custom way,
# before opening the notebook
# Should return a redirect to /edit?id={notebook_id}
# Note: use Pluto.open - try_launch_notebook_response will return a fitting response.
struct CustomLaunchEvent <: PlutoEvent
    params::Dict{Any, Any}
    request::HTTP.Request
    try_launch_notebook_response::Function
end

# Triggered when a notebook has shut down.
struct ShutdownNotebookEvent <: PlutoEvent
    notebook::Notebook
end
