#!/usr/bin/env ruby
# frozen_string_literal: true

require "json"
require "net/http"
require_relative "../lib/mcp"

project_name = ENV["COSENSE_PROJECT_NAME"] || "funwarioisii"
cosense_sid = ENV["COSENSE_SID"]

# Cosense API Client
module CosenseClient
  BASE_URL = "https://scrapbox.io/api"
  TIMEOUT = 5 # seconds

  def self.get_page(project_name, page_name, sid = nil)
    encoded_page_name = URI.encode_www_form_component(page_name.tr(" ", "_"))
    warn "Fetching page: #{project_name}/#{page_name}"
    url = "#{BASE_URL}/pages/#{project_name}/#{encoded_page_name}"
    make_request(url, sid)
  end

  def self.list_pages(project_name, sid = nil)
    make_request("#{BASE_URL}/pages/#{project_name}", sid)
  end

  def self.to_readable_page(page)
    return {title: "Error", description: "Page not found or error occurred"} unless page

    title_and_description = <<~TEXT
      #{page[:title]}
      ---
      #{page[:lines].map { |line| line[:text] }.join("\n")}
    TEXT

    related_pages = if page[:links]&.any?
      <<~TEXT
        ## 関連するページのタイトル
        #{page[:links].join("\n")}
      TEXT
    else
      ""
    end

    {
      title: page[:title],
      description: title_and_description + related_pages
    }
  end

  def self.make_request(url, sid = nil)
    uri = URI(url)
    headers = sid ? {"Cookie" => "connect.sid=#{sid}"} : {}

    begin
      response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
        http.open_timeout = TIMEOUT
        http.read_timeout = TIMEOUT
        request = Net::HTTP::Get.new(uri, headers)
        http.request(request)
      end
      warn "Response status: #{response.code} #{response.message}"

      return nil unless response.is_a?(Net::HTTPSuccess)
      JSON.parse(response.body, symbolize_names: true)
    rescue JSON::ParserError => e
      warn "JSON parse error: #{e.message}"
      nil
    rescue => e
      warn "Error: #{e.class} - #{e.message}"
      warn e.backtrace.join("\n")
      nil
    ensure
      response&.body&.close if response&.body.respond_to?(:close)
    end
  end
end

# Start of main content

name "cosense-mcp-server"

# Get page list and register as resources
cosense_resources = CosenseClient.list_pages(project_name, cosense_sid)

if cosense_resources
  cosense_resources[:pages].each do |page|
    resource "cosense://#{page[:title]}" do
      name page[:title]
      description "A text page: #{page[:title]}"
      call do
        page_data = CosenseClient.get_page(project_name, page[:title], cosense_sid)
        readable_page = CosenseClient.to_readable_page(page_data)
        readable_page[:description]
      end
    end
  end
else
  warn "No pages found or error occurred while fetching pages"
end

# ツールの定義
tool "get_page" do
  description <<~DESC
    Get a page from #{project_name} project on cosen.se
    In cosense, a page is a cosense-style document with a title and a description.
    Bracket Notation makes links between pages.

    Example:
      [Page Title] -> "/#{project_name}/Page Title"
      [https://example.com] -> "https://example.com"
      [example https://example.com] -> "https://example.com"

    A page may have links to other pages.
    Links are rendered at the bottom of the page.
  DESC

  argument :page_title, String, required: true, description: "Title of the page"

  call do |args|
    page = CosenseClient.get_page(project_name, args[:page_title], cosense_sid)
    raise "Page #{args[:page_title]} not found" unless page

    readable_page = CosenseClient.to_readable_page(page)

    readable_page[:description]
  end
end
