import { 
    ConfigParams, Descriptor, FilterParams, 
    IConfigurable, IReferenceable, IReferences, 
    IStringIdentifiable 
} from "pip-services3-commons-nodex";
import { Factory } from "pip-services3-components-nodex";
import { ProcessContainer } from "pip-services3-container-nodex";
import { IdentifiableMySqlPersistence } from "pip-services3-mysql-nodex";
import { DefaultRpcFactory, RestService } from "pip-services3-rpc-nodex";

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

// Tier 1: View
export class HelloFriendRestService extends RestService {
    protected controller: HelloFriendController;

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

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

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

    private async greeting(req: any, res: any): Promise<void> {
        let result = await this.controller.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.controller.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 : Controller
export class HelloFriendController 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(correlationId: string, item: MyFriend): Promise<MyFriend>  {
        let res = await this.persistence.create(correlationId, 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(correlationId: string, filter: any): Promise<MyFriend> {
        return super.getOneRandom(correlationId, this.composeFilter(filter));
    }
}

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

        this.registerAsType(HttpServiceDescriptor, HelloFriendRestService); // View
        this.registerAsType(ControllerDescriptor,  HelloFriendController);  // Controller
        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 DefaultRpcFactory());
    }
}
        
// Running the app
export async function main() { 
    try {
        let proc = new HelloFriendProcess();
        proc.run(process.argv);
    } catch (ex) {
        console.error(ex);
    }
}