package installer

import (
	"bytes"
	"encoding/json"
	"fmt"
	"testing"

	"cuelang.org/go/cue/errors"
)

var exposed = `
input: {
  key: {
    private: "asd"
    public: "qwe"
  }
}
service: [{
    name: "app"
	type: "golang:1.20.0"
	run: "main.go"
	ingress: [{
		network: "private"
		subdomain: "testapp"
		auth: enabled: false
        port: name: "web"
	}]
    source: repository: "ssh://foo.bar"
    ports: [{
        name: "a"
        value: 100
    }, {
		name: "web"
		value: 8080
    }, {
		name: "b"
		value: 101
    }]
    expose: [{
		network: "private"
		subdomain: "1"
        port: value: 100
	}, {
		network: "public"
		subdomain:"2"
        port: name: "web"
	}, {
		network: "private"
		subdomain: "3"
        port: value: 101
    }],
    dev: enabled: false
}]

postgresql: [{
	name: "db_1"
	expose: [{
		network: "private"
		subdomain: "1"
	}, {
		network: "public"
		subdomain:"2"
	}, {
		network: "private"
		subdomain: "3"
	}]
}, {
	name: "db_2"
	expose: [{
		network: "public"
		subdomain: "1"
	}, {
		network: "private"
		subdomain:"2"
	}, {
		network: "public"
		subdomain: "3"
	}]
}]

mongodb: [{
	name: "mdb_1"
	expose: [{
		network: "public"
		subdomain: "1"
	}, {
		network: "private"
		subdomain:"2"
	}, {
		network: "public"
		subdomain: "3"
	}]
}, {
	name: "mdb_2"
	expose: [{
		network: "private"
		subdomain: "1"
	}, {
		network: "public"
		subdomain:"2"
	}, {
		network: "private"
		subdomain: "3"
	}]
}]
`

func TestExposedPorts(t *testing.T) {
	app, err := NewDodoApp([]byte(exposed))
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, nil, map[string]any{
		"managerAddr":            "",
		"appId":                  "",
		"sshPrivateKey":          "",
		"port_postgresql_db_1_0": 1,
		"port_postgresql_db_1_1": 2,
		"port_postgresql_db_1_2": 3,
		"port_postgresql_db_2_0": 4,
		"port_postgresql_db_2_1": 5,
		"port_postgresql_db_2_2": 6,
		"port_mongodb_mdb_1_0":   1,
		"port_mongodb_mdb_1_1":   2,
		"port_mongodb_mdb_1_2":   3,
		"port_mongodb_mdb_2_0":   4,
		"port_mongodb_mdb_2_1":   5,
		"port_mongodb_mdb_2_2":   6,
		"port_service_app_0":     7,
		"port_service_app_1":     8,
		"port_service_app_2":     9,
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}

const canvas = `
{
  "service": [
    {
      "type": "nextjs:deno-2.0.0",
      "name": "app",
      "source": {
        "repository": "ssh://d.p.v1.dodo.cloud:62533/myblog",
        "branch": "master",
        "rootDir": "/"
      },
      "ports": [
        {
          "name": "web",
          "value": 3000,
          "protocol": "TCP"
        }
      ],
      "env": [
        {
          "name": "DODO_POSTGRESQL_DB_CONNECTION_URL"
        }
      ],
      "ingress": [
        {
          "network": "Private",
          "subdomain": "foo",
          "port": {
            "name": "web"
          },
          "auth": {
            "enabled": false
          }
        },
        {
          "network": "Public",
          "subdomain": "foo",
          "port": {
            "name": "web"
          },
          "auth": {
            "enabled": false
          }
        }
      ],
      "expose": [],
      "dev": {
        "enabled": true,
        "mode": "VM",
        "username": "gio"
      }
    }
  ],
  "volume": [],
  "postgresql": [
    {
      "name": "db",
      "size": "1Gi",
      "expose": []
    }
  ],
  "mongodb": []
}
`

func TestCanvas(t *testing.T) {
	app, err := NewDodoApp([]byte(canvas))
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, nil, map[string]any{
		"managerAddr":   "",
		"appId":         "",
		"sshPrivateKey": "",
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}

const exposeRemoteClusterPrivateNetwork = `
{
    "cluster": "remote",
    "postgresql": [{
		"name": "db",
		"size": "1Gi",
		"expose": [{
			"network": "Private",
			"subdomain": "pg"
		}]
	}],
}
`

func TestExposeRemoteClusterPrivateNetwork(t *testing.T) {
	var buf bytes.Buffer
	if _, err := buf.WriteString(exposeRemoteClusterPrivateNetwork); err != nil {
		t.Fatal(err)
	}
	clusters := []Cluster{{
		Name:             "remote",
		Kubeconfig:       "<KUBECONFIG>",
		IngressClassName: "<INGRESS_CLASS_NAME>",
	}}
	if err := json.NewEncoder(&buf).Encode(struct {
		Clusters []Cluster `json:"clusters"`
	}{
		clusters,
	}); err != nil {
		t.Fatal(err)
	}
	app, err := NewDodoApp(buf.Bytes())
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, clusters, map[string]any{
		"managerAddr":                  "",
		"appId":                        "",
		"sshPrivateKey":                "",
		"port_postgresql_db_0":         1,
		"port_postgresql_db_0_cluster": 2,
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
	t.Log(fmt.Sprintf("%+v", r.Ports))
}

const exposeRemoteClusterPublicNetwork = `
{
    "cluster": "remote",
    "postgresql": [{
		"name": "db",
		"size": "1Gi",
		"expose": [{
			"network": "Public",
			"subdomain": "pg"
		}]
	}],
}
`

func TestExposeRemoteClusterPublicNetwork(t *testing.T) {
	var buf bytes.Buffer
	if _, err := buf.WriteString(exposeRemoteClusterPublicNetwork); err != nil {
		t.Fatal(err)
	}
	clusters := []Cluster{{
		Name:             "remote",
		Kubeconfig:       "<KUBECONFIG>",
		IngressClassName: "<INGRESS_CLASS_NAME>",
	}}
	if err := json.NewEncoder(&buf).Encode(struct {
		Clusters []Cluster `json:"clusters"`
	}{
		clusters,
	}); err != nil {
		t.Fatal(err)
	}
	app, err := NewDodoApp(buf.Bytes())
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, clusters, map[string]any{
		"managerAddr":                  "",
		"appId":                        "",
		"sshPrivateKey":                "",
		"port_postgresql_db_0":         1,
		"port_postgresql_db_0_cluster": 2,
		"port_postgresql_db_0_private": 3,
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}

const exposeSVCRemoteCluster = `
{
  "cluster": "remote",
  "service": [
    {
      "name": "echo",
      "type": "golang:1.20.0",
      "source": {
        "repository": "ssh://foo.bar"
      },
      "ports": [
        {
          "name": "echo",
          "value": 9090
        }
      ],
      "expose": [
        {
          "port": {
            "name": "echo"
          },
          "network": "Private",
          "subdomain": "echo"
        }
      ],
      "dev": {
        "enabled": false
      }
    }
  ]
}
`

func TestExposeSVCRemoteCluster(t *testing.T) {
	var buf bytes.Buffer
	if _, err := buf.WriteString(exposeSVCRemoteCluster); err != nil {
		t.Fatal(err)
	}
	clusters := []Cluster{{
		Name:             "remote",
		Kubeconfig:       "<KUBECONFIG>",
		IngressClassName: "<INGRESS_CLASS_NAME>",
	}}
	if err := json.NewEncoder(&buf).Encode(struct {
		Clusters []Cluster `json:"clusters"`
	}{
		clusters,
	}); err != nil {
		t.Fatal(err)
	}
	app, err := NewDodoApp(buf.Bytes())
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, clusters, map[string]any{
		"managerAddr":                 "",
		"appId":                       "",
		"sshPrivateKey":               "",
		"port_service_echo_0":         1,
		"port_service_echo_0_cluster": 2,
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
	t.Log(fmt.Sprintf("%+v", r.Ports))
}

const sshCodeServer = `
{
  "service": [
    {
      "type": "nextjs:deno-2.0.0",
      "name": "app",
      "source": {
        "repository": "ssh://d.p.v1.dodo.cloud:62533/myblog",
        "branch": "master",
        "rootDir": "/"
      },
      "ports": [
        {
          "name": "web",
          "value": 3000,
          "protocol": "TCP"
        }
      ],
      "env": [
        {
          "name": "DODO_POSTGRESQL_DB_CONNECTION_URL"
        }
      ],
      "ingress": [
        {
          "network": "Private",
          "subdomain": "foo",
          "port": {
            "name": "web"
          },
          "auth": {
            "enabled": false
          }
        },
        {
          "network": "Public",
          "subdomain": "foo",
          "port": {
            "name": "web"
          },
          "auth": {
            "enabled": false
          }
        }
      ],
      "expose": [{
		"network": "Public",
		"subdomain": "foo",
		"port": {
		  "name": "web"
		},
      }],
      "dev": {
        "enabled": true,
        "mode": "VM",
        "username": "gio",
        "codeServer": {
          "network": "Private",
          "subdomain": "code"
        },
        "ssh": {
          "network": "Public",
          "subdomain": "ssh"
        }
      }
    }
  ],
  "volume": [],
  "postgresql": [
    {
      "name": "db",
      "size": "1Gi",
      "expose": []
    }
  ],
  "mongodb": []
}
`

func TestSSHCodeServer(t *testing.T) {
	app, err := NewDodoApp([]byte(sshCodeServer))
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, nil, map[string]any{
		"managerAddr":          "",
		"appId":                "",
		"sshPrivateKey":        "",
		"port_service_app_ssh": 12,
		"port_service_app_0":   13,
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}

const foo = `
{
	"service": [
		{
			"type": "deno:2.2.0",
			"name": "qwe",
			"source": {
				"repository": "git@github.com:giolekva/dodo-blog.git"
			},
			"ports": [
				{
					"name": "web",
					"value": 8081,
					"protocol": "TCP"
				}
			],
			"env": [
				{
					"name": "DODO_POSTGRESQL_DB_URL"
				},
				{
					"name": "DODO_PORT_WEB"
				}
			],
			"ingress": [
				{
					"network": "Private",
					"subdomain": "blog",
					"port": {
						"name": "web"
					},
					"auth": {
						"enabled": false
					}
				}
			],
			"expose": [
				{
					"network": "Private",
					"subdomain": "blog",
					"port": {
						"name": "web"
					}
				}
			],
			"preBuildCommands": [],
			"dev": {
				"enabled": true,
				"mode": "PROXY",
				"vpn": { "enabled": true, "username": "gio" },
				"address": "65.108.39.172"
			}
		}
	],
	"volume": [],
	"postgresql": [],
	"mongodb": []
}
`

func TestFoo(t *testing.T) {
	app, err := NewDodoApp([]byte(foo))
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, nil, map[string]any{
		"managerAddr":          "",
		"appId":                "",
		"sshPrivateKey":        "",
		"port_service_qwe_ssh": 1,
		"port_service_qwe_0":   2,
		"port_postgresql_db_0": 3,
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}

const sketch = `
{
  "agent": [
    {
      "type": "sketch:latest",
      "name": "dev",
      "model": {
        "name": "gemini",
        "geminiApiKey": "foo"
      },
      "ingress": [{
        "network": "private",
        "subdomain": "foo",
        "port": {
          "name": "agent"
        }
      }],
      "ports": [
        {
          "name": "agent",
          "value": 2001,
          "protocol": "TCP"
        },
        {
          "name": "p8080",
          "value": 8080,
          "protocol": "TCP"
        },
        {
          "name": "p8081",
          "value": 8081,
          "protocol": "TCP"
        },
        {
          "name": "p8082",
          "value": 8082,
          "protocol": "TCP"
        },
        {
          "name": "p8083",
          "value": 8083,
          "protocol": "TCP"
        },
        {
          "name": "p8084",
          "value": 8084,
          "protocol": "TCP"
        }
      ]
    }
  ]
}
`

func TestSketch(t *testing.T) {
	app, err := NewDodoApp([]byte(sketch))
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, nil, map[string]any{
		"managerAddr":  "",
		"appId":        "",
		"geminiApiKey": "dev",
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}

const sketchGlobalGeminiApiKey = `
{
    "agent": [
        {
            "type": "sketch:latest",
            "name": "dev",
            "model": {
                "name": "gemini"
            }
        }
    ]
}
`

func TestSketchGlobalGeminiApiKey(t *testing.T) {
	app, err := NewDodoApp([]byte(sketchGlobalGeminiApiKey))
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, nil, map[string]any{
		"managerAddr":  "",
		"appId":        "",
		"geminiApiKey": "dev",
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}

const serviceAndAgent = `
{
  "service": [
    {
      "nodeId": "02efbce4-c338-4cb9-a101-63acfeaca4c7",
      "type": "deno:2.2.0",
      "name": "blog",
      "source": {
        "repository": "git@github.com:giolekva/dodo-blog.git",
        "branch": "master",
        "rootDir": "/"
      },
      "ports": [
        {
          "name": "web",
          "value": 8080,
          "protocol": "TCP"
        }
      ],
      "env": [
        {
          "name": "DODO_POSTGRESQL_DB_URL"
        },
        {
          "name": "DODO_PORT_WEB",
          "alias": "PORT"
        }
      ],
      "ingress": [
        {
          "nodeId": "bb4c754a-d50b-4686-b485-17ad1804f014",
          "network": "Private",
          "subdomain": "blog",
          "port": {
            "name": "web"
          },
          "auth": {
            "enabled": false
          }
        }
      ],
      "expose": [],
      "preBuildCommands": [
        {
          "bin": "deno run -A npm:prisma migrate dev"
        }
      ],
      "dev": {
        "enabled": false
      }
    }
  ],
  "agent": [
    {
      "nodeId": "76081511-fa20-4202-935a-a171c79a9daf",
      "type": "sketch:latest",
      "name": "lead",
      "model": {
        "name": "gemini"
      },
      "ports": [
        {
          "name": "agent",
          "value": 2001,
          "protocol": "TCP"
        },
        {
          "name": "p8080",
          "value": 8080,
          "protocol": "TCP"
        },
        {
          "name": "p8081",
          "value": 8081,
          "protocol": "TCP"
        },
        {
          "name": "p8082",
          "value": 8082,
          "protocol": "TCP"
        },
        {
          "name": "p8083",
          "value": 8083,
          "protocol": "TCP"
        },
        {
          "name": "p8084",
          "value": 8084,
          "protocol": "TCP"
        }
      ],
      "env": [
        {
          "name": "DODO_PORT_AGENT"
        },
        {
          "name": "DODO_PORT_P8080"
        },
        {
          "name": "DODO_PORT_P8081"
        },
        {
          "name": "DODO_PORT_P8082"
        },
        {
          "name": "DODO_PORT_P8083"
        },
        {
          "name": "DODO_PORT_P8084"
        }
      ],
      "ingress": [
        {
          "nodeId": "31cadf3f-0858-4a86-844b-733910817984",
          "network": "Private",
          "subdomain": "weq",
          "port": {
            "name": "agent"
          },
          "auth": {
            "enabled": false
          }
        }
      ],
      "expose": [],
      "preBuildCommands": [],
      "dev": {
        "enabled": false
      }
    }
  ],
  "volume": [],
  "postgresql": [
    {
      "nodeId": "ba62db4b-2b07-49d0-ba29-ab65bd244997",
      "name": "db",
      "size": "1Gi",
      "expose": []
    }
  ],
  "mongodb": []
}
`

func TestServiceAndAgent(t *testing.T) {
	app, err := NewDodoApp([]byte(serviceAndAgent))
	if err != nil {
		for _, e := range errors.Errors(err) {
			t.Log(e)
		}
		t.Fatal(err)
	}
	release := Release{
		Namespace:     "foo",
		AppInstanceId: "foo-bar",
		RepoAddr:      "ssh://192.168.100.210:22/config",
		AppDir:        "/foo/bar",
	}
	keyGen := testKeyGen{}
	r, err := app.Render(release, env, networks, nil, map[string]any{
		"managerAddr":  "",
		"appId":        "",
		"geminiApiKey": "dev",
	}, nil, keyGen)
	if err != nil {
		t.Fatal(err)
	}
	t.Log(string(r.Raw))
}
