import { 
    ConfigParams, Descriptor, IConfigurable, 
    IReferenceable, IReferences, Context, Factory 
} from "pip-services4-components-node";
import { ProcessContainer } from "pip-services4-container-node";
import { RestController, DefaultHttpFactory } from "pip-services4-http-node";
import { IdentifiableMySqlPersistence } from "pip-services4-mysql-node";
import { IStringIdentifiable, FilterParams } from "pip-services4-data-node";


// Data object
export class MyFriend implements IStringIdentifiable {
    public id: string;
    public type: string;
    public name: string;
}

// Tier 1: Controller
export class HelloFriendRestController extends RestController {
    protected service: HelloFriendService;

    public constructor() {
        super()
        this._baseRoute = "/hello_friend";
    }

    public configure(config: ConfigParams): void {
        super.configure(config);
    }

    public setReferences(references: IReferences): void {
        super.setReferences(references);
        this.service = references.getOneRequired(new Descriptor("hello-friend", "service", "*", "*", "1.0"));
    }

    private async greeting(req: any, res: any): Promise<void> {
        let result = await this.service.greeting();
        await this.sendResult(req, res, result);
    }

    private async create(req: any, res: any): Promise<void> {
        let correlationId = this.getCorrelationId(req);
        let friend: MyFriend = req.query;
        let result = await this.service.create(correlationId, friend);

        await this.sendResult(req, res, result);
    }

    public register(): void {
        this.registerRoute("GET", "/greeting", null, this.greeting);
        this.registerRoute("GET", "/greeting_create", null, this.create);
    }
}


// Tier 2 : Service
export class HelloFriendService implements IConfigurable, IReferenceable {
    private defaultName: string;
    private persistence: HelloFriendPersistence;

    public constructor() {
        this.defaultName = "Pip User";
    }

    public configure(config: ConfigParams): void {
        this.defaultName = config.getAsStringWithDefault("default_name", this.defaultName);
    }

    public setReferences(references: IReferences): void {
        this.persistence = references.getOneRequired(new Descriptor("hello-friend", "persistence", "*", "*", "1.0"));
    }

    public async greeting(): Promise<string> {
        let filter = FilterParams.fromTuples("type", "friend");
        let selectedFilter = await this.persistence.getOneRandom(null, filter);
        let name = selectedFilter != null ? selectedFilter.name : null;

        return `Hello, ${name} !`;
    }

    public async create(ctx: Context, item: MyFriend): Promise<MyFriend>  {
        let res = await this.persistence.create(ctx, item);
        return res;
    }
}

    
// Tier 3 = Persistence
export class HelloFriendPersistence extends IdentifiableMySqlPersistence<MyFriend, string> {
    public constructor() {
        super("myfriends3");
    }

    protected defineSchema(): void {
        this.clearSchema();
        this.ensureSchema('CREATE TABLE IF NOT EXISTS `' + this._tableName + '` (id VARCHAR(32) PRIMARY KEY, `type` VARCHAR(50), `name` TEXT)');
    }

    private composeFilter(filter: FilterParams): string {
        filter ??= new FilterParams();
        let type = filter.getAsNullableString("type");
        let name = filter.getAsNullableString("name");

        let filterCondition = "";
        if (type != null)
            filterCondition += "type='" + type + "'";
        if (name != null)
            filterCondition += "name='" + name + "'";

        return filterCondition;
    }

    public getOneRandom(ctx: Context, filter: any): Promise<MyFriend> {
        return super.getOneRandom(ctx, this.composeFilter(filter));
    }
}

// Inversion of control: Factory
export class HelloFriendServiceFactory extends Factory {
    public constructor() {
        super();
        let HttpControllerDescriptor = new Descriptor("hello-friend", "controller", "http", "*", "1.0");      // Controller
        let ServiceDescriptor = new Descriptor("hello-friend", "service", "default", "*", "1.0"); // Service
        let PersistenceDescriptor = new Descriptor("hello-friend", "persistence", "mysql", "*", "1.0"); // Persistence

        this.registerAsType(HttpControllerDescriptor, HelloFriendRestController); // Controller
        this.registerAsType(ServiceDescriptor,  HelloFriendService);  // service
        this.registerAsType(PersistenceDescriptor, HelloFriendPersistence); // Persistence
    }
}


// Containerization
export class HelloFriendProcess extends ProcessContainer {
    public constructor() {
        super("hello-friend", "HelloFriend microservice");
        this._configPath = "./config.yaml"
        this._factories.add(new HelloFriendServiceFactory());
        this._factories.add(new DefaultHttpFactory());
    }
}
        
// Running the app
export async function main() { 
    try {
        let proc = new HelloFriendProcess();
        proc.run(process.argv);
    } catch (ex) {
        console.error(ex);
    }
}